michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: // This component is used for handling dragover and drop of urls. michael@0: // michael@0: // It checks to see whether a drop of a url is allowed. For instance, a url michael@0: // cannot be dropped if it is not a valid uri or the source of the drag cannot michael@0: // access the uri. This prevents, for example, a source document from tricking michael@0: // the user into dragging a chrome url. michael@0: michael@0: function ContentAreaDropListener() { }; michael@0: michael@0: ContentAreaDropListener.prototype = michael@0: { michael@0: classID: Components.ID("{1f34bc80-1bc7-11d6-a384-d705dd0746fc}"), michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIDroppedLinkHandler, Ci.nsISupports]), michael@0: michael@0: _getDropURL : function (dt) michael@0: { michael@0: let types = dt.types; michael@0: for (let t = 0; t < types.length; t++) { michael@0: let type = types[t]; michael@0: switch (type) { michael@0: case "text/uri-list": michael@0: var url = dt.getData("URL").replace(/^\s+|\s+$/g, ""); michael@0: return [url, url]; michael@0: case "text/plain": michael@0: case "text/x-moz-text-internal": michael@0: var url = dt.getData(type).replace(/^\s+|\s+$/g, ""); michael@0: return [url, url]; michael@0: case "text/x-moz-url": michael@0: return dt.getData(type).split("\n"); michael@0: } michael@0: } michael@0: michael@0: // For shortcuts, we want to check for the file type last, so that the michael@0: // url pointed to in one of the url types is found first before the file michael@0: // type, which points to the actual file. michael@0: let file = dt.mozGetDataAt("application/x-moz-file", 0); michael@0: if (file instanceof Ci.nsIFile) { michael@0: let ioService = Cc["@mozilla.org/network/io-service;1"]. michael@0: getService(Ci.nsIIOService); michael@0: let fileHandler = ioService.getProtocolHandler("file") michael@0: .QueryInterface(Ci.nsIFileProtocolHandler); michael@0: return [fileHandler.getURLSpecFromFile(file), file.leafName]; michael@0: } michael@0: michael@0: return [ ]; michael@0: }, michael@0: michael@0: _validateURI: function(dataTransfer, uriString, disallowInherit) michael@0: { michael@0: if (!uriString) michael@0: return ""; michael@0: michael@0: // Strip leading and trailing whitespace, then try to create a michael@0: // URI from the dropped string. If that succeeds, we're michael@0: // dropping a URI and we need to do a security check to make michael@0: // sure the source document can load the dropped URI. michael@0: uriString = uriString.replace(/^\s*|\s*$/g, ''); michael@0: michael@0: let uri; michael@0: let ioService = Cc["@mozilla.org/network/io-service;1"] michael@0: .getService(Components.interfaces.nsIIOService); michael@0: try { michael@0: // Check that the uri is valid first and return an empty string if not. michael@0: // It may just be plain text and should be ignored here michael@0: uri = ioService.newURI(uriString, null, null); michael@0: } catch (ex) { } michael@0: if (!uri) michael@0: return uriString; michael@0: michael@0: // uriString is a valid URI, so do the security check. michael@0: let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]. michael@0: getService(Ci.nsIScriptSecurityManager); michael@0: let sourceNode = dataTransfer.mozSourceNode; michael@0: let flags = secMan.STANDARD; michael@0: if (disallowInherit) michael@0: flags |= secMan.DISALLOW_INHERIT_PRINCIPAL; michael@0: michael@0: // Use file:/// as the default uri so that drops of file URIs are always allowed michael@0: let principal = sourceNode ? sourceNode.nodePrincipal michael@0: : secMan.getSimpleCodebasePrincipal(ioService.newURI("file:///", null, null)); michael@0: michael@0: secMan.checkLoadURIStrWithPrincipal(principal, uriString, flags); michael@0: michael@0: return uriString; michael@0: }, michael@0: michael@0: canDropLink: function(aEvent, aAllowSameDocument) michael@0: { michael@0: if (this._eventTargetIsDisabled(aEvent)) michael@0: return false; michael@0: michael@0: let dataTransfer = aEvent.dataTransfer; michael@0: let types = dataTransfer.types; michael@0: if (!types.contains("application/x-moz-file") && michael@0: !types.contains("text/x-moz-url") && michael@0: !types.contains("text/uri-list") && michael@0: !types.contains("text/x-moz-text-internal") && michael@0: !types.contains("text/plain")) michael@0: return false; michael@0: michael@0: if (aAllowSameDocument) michael@0: return true; michael@0: michael@0: let sourceNode = dataTransfer.mozSourceNode; michael@0: if (!sourceNode) michael@0: return true; michael@0: michael@0: // don't allow a drop of a node from the same document onto this one michael@0: let sourceDocument = sourceNode.ownerDocument; michael@0: let eventDocument = aEvent.originalTarget.ownerDocument; michael@0: if (sourceDocument == eventDocument) michael@0: return false; michael@0: michael@0: // also check for nodes in other child or sibling frames by checking michael@0: // if both have the same top window. michael@0: if (sourceDocument && eventDocument) { michael@0: let sourceRoot = sourceDocument.defaultView.top; michael@0: if (sourceRoot && sourceRoot == eventDocument.defaultView.top) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: }, michael@0: michael@0: dropLink: function(aEvent, aName, aDisallowInherit) michael@0: { michael@0: aName.value = ""; michael@0: if (this._eventTargetIsDisabled(aEvent)) michael@0: return ""; michael@0: michael@0: let dataTransfer = aEvent.dataTransfer; michael@0: let [url, name] = this._getDropURL(dataTransfer); michael@0: michael@0: try { michael@0: url = this._validateURI(dataTransfer, url, aDisallowInherit); michael@0: } catch (ex) { michael@0: aEvent.stopPropagation(); michael@0: aEvent.preventDefault(); michael@0: throw ex; michael@0: } michael@0: michael@0: if (name) michael@0: aName.value = name; michael@0: michael@0: return url; michael@0: }, michael@0: michael@0: _eventTargetIsDisabled: function(aEvent) michael@0: { michael@0: let ownerDoc = aEvent.originalTarget.ownerDocument; michael@0: if (!ownerDoc || !ownerDoc.defaultView) michael@0: return false; michael@0: michael@0: return ownerDoc.defaultView michael@0: .QueryInterface(Components.interfaces.nsIInterfaceRequestor) michael@0: .getInterface(Components.interfaces.nsIDOMWindowUtils) michael@0: .isNodeDisabledForEvents(aEvent.originalTarget); michael@0: } michael@0: }; michael@0: michael@0: var components = [ContentAreaDropListener]; michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);