diff options
Diffstat (limited to 'addon-sdk/source/lib/framescript')
-rw-r--r-- | addon-sdk/source/lib/framescript/FrameScriptManager.jsm | 27 | ||||
-rw-r--r-- | addon-sdk/source/lib/framescript/content.jsm | 94 | ||||
-rw-r--r-- | addon-sdk/source/lib/framescript/context-menu.js | 215 | ||||
-rw-r--r-- | addon-sdk/source/lib/framescript/manager.js | 26 | ||||
-rw-r--r-- | addon-sdk/source/lib/framescript/util.js | 25 |
5 files changed, 0 insertions, 387 deletions
diff --git a/addon-sdk/source/lib/framescript/FrameScriptManager.jsm b/addon-sdk/source/lib/framescript/FrameScriptManager.jsm deleted file mode 100644 index 1ce6ceb07..000000000 --- a/addon-sdk/source/lib/framescript/FrameScriptManager.jsm +++ /dev/null @@ -1,27 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const globalMM = Components.classes["@mozilla.org/globalmessagemanager;1"]. - getService(Components.interfaces.nsIMessageListenerManager); - -// Load frame scripts from the same dir as this module. -// Since this JSM will be loaded using require(), PATH will be -// overridden while running tests, just like any other module. -const PATH = __URI__.replace('framescript/FrameScriptManager.jsm', ''); - -// Builds a unique loader ID for this runtime. We prefix with the SDK path so -// overriden versions of the SDK don't conflict -var LOADER_ID = 0; -this.getNewLoaderID = () => { - return PATH + ":" + LOADER_ID++; -} - -const frame_script = function(contentFrame, PATH) { - let { registerContentFrame } = Components.utils.import(PATH + 'framescript/content.jsm', {}); - registerContentFrame(contentFrame); -} -globalMM.loadFrameScript("data:,(" + frame_script.toString() + ")(this, " + JSON.stringify(PATH) + ");", true); - -this.EXPORTED_SYMBOLS = ['getNewLoaderID']; diff --git a/addon-sdk/source/lib/framescript/content.jsm b/addon-sdk/source/lib/framescript/content.jsm deleted file mode 100644 index eaee26be3..000000000 --- a/addon-sdk/source/lib/framescript/content.jsm +++ /dev/null @@ -1,94 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { utils: Cu, classes: Cc, interfaces: Ci } = Components; -const { Services } = Cu.import('resource://gre/modules/Services.jsm'); - -const cpmm = Cc['@mozilla.org/childprocessmessagemanager;1']. - getService(Ci.nsISyncMessageSender); - -this.EXPORTED_SYMBOLS = ["registerContentFrame"]; - -// This may be an overriden version of the SDK so use the PATH as a key for the -// initial messages before we have a loaderID. -const PATH = __URI__.replace('framescript/content.jsm', ''); - -const { Loader } = Cu.import(PATH + 'toolkit/loader.js', {}); - -// one Loader instance per addon (per @loader/options to be precise) -var addons = new Map(); - -// Tell the parent that a new process is ready -cpmm.sendAsyncMessage('sdk/remote/process/start', { - modulePath: PATH -}); - -// Load a child process module loader with the given loader options -cpmm.addMessageListener('sdk/remote/process/load', ({ data: { modulePath, loaderID, options, reason } }) => { - if (modulePath != PATH) - return; - - // During startup races can mean we get a second load message - if (addons.has(loaderID)) - return; - - options.waiveInterposition = true; - - let loader = Loader.Loader(options); - let addon = { - loader, - require: Loader.Require(loader, { id: 'LoaderHelper' }), - } - addons.set(loaderID, addon); - - cpmm.sendAsyncMessage('sdk/remote/process/attach', { - loaderID, - processID: Services.appinfo.processID, - isRemote: Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT - }); - - addon.child = addon.require('sdk/remote/child'); - - for (let contentFrame of frames.values()) - addon.child.registerContentFrame(contentFrame); -}); - -// Unload a child process loader -cpmm.addMessageListener('sdk/remote/process/unload', ({ data: { loaderID, reason } }) => { - if (!addons.has(loaderID)) - return; - - let addon = addons.get(loaderID); - Loader.unload(addon.loader, reason); - - // We want to drop the reference to the loader but never allow creating a new - // loader with the same ID - addons.set(loaderID, {}); -}) - - -var frames = new Set(); - -this.registerContentFrame = contentFrame => { - contentFrame.addEventListener("unload", () => { - unregisterContentFrame(contentFrame); - }, false); - - frames.add(contentFrame); - - for (let addon of addons.values()) { - if ("child" in addon) - addon.child.registerContentFrame(contentFrame); - } -}; - -function unregisterContentFrame(contentFrame) { - frames.delete(contentFrame); - - for (let addon of addons.values()) { - if ("child" in addon) - addon.child.unregisterContentFrame(contentFrame); - } -} diff --git a/addon-sdk/source/lib/framescript/context-menu.js b/addon-sdk/source/lib/framescript/context-menu.js deleted file mode 100644 index 3915b7cd8..000000000 --- a/addon-sdk/source/lib/framescript/context-menu.js +++ /dev/null @@ -1,215 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { query, constant, cache } = require("sdk/lang/functional"); -const { pairs, each, map, object } = require("sdk/util/sequence"); -const { nodeToMessageManager } = require("./util"); - -// Decorator function that takes `f` function and returns one that attempts -// to run `f` with given arguments. In case of exception error is logged -// and `fallback` is returned instead. -const Try = (fn, fallback=null) => (...args) => { - try { - return fn(...args); - } catch(error) { - console.error(error); - return fallback; - } -}; - -// Decorator funciton that takes `f` function and returns one that returns -// JSON cloned result of whatever `f` returns for given arguments. -const JSONReturn = f => (...args) => JSON.parse(JSON.stringify(f(...args))); - -const Null = constant(null); - -// Table of readers mapped to field names they're going to be reading. -const readers = Object.create(null); -// Read function takes "contextmenu" event target `node` and returns table of -// read field names mapped to appropriate values. Read uses above defined read -// table to read data for all registered readers. -const read = node => - object(...map(([id, read]) => [id, read(node, id)], pairs(readers))); - -// Table of built-in readers, each takes a descriptor and returns a reader: -// descriptor -> node -> JSON -const parsers = Object.create(null) -// Function takes a descriptor of the remotely defined reader and parsese it -// to construct a local reader that's going to read out data from context menu -// target. -const parse = descriptor => { - const parser = parsers[descriptor.category]; - if (!parser) { - console.error("Unknown reader descriptor was received", descriptor, `"${descriptor.category}"`); - return Null - } - return Try(parser(descriptor)); -} - -// TODO: Test how chrome's mediaType behaves to try and match it's behavior. -const HTML_NS = "http://www.w3.org/1999/xhtml"; -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -const SVG_NS = "http://www.w3.org/2000/svg"; - -// Firefox always creates a HTMLVideoElement when loading an ogg file -// directly. If the media is actually audio, be smarter and provide a -// context menu with audio operations. -// Source: https://github.com/mozilla/gecko-dev/blob/28c2fca3753c5371643843fc2f2f205146b083b7/browser/base/content/nsContextMenu.js#L632-L637 -const isVideoLoadingAudio = node => - node.readyState >= node.HAVE_METADATA && - (node.videoWidth == 0 || node.videoHeight == 0) - -const isVideo = node => - node instanceof node.ownerDocument.defaultView.HTMLVideoElement && - !isVideoLoadingAudio(node); - -const isAudio = node => { - const {HTMLVideoElement, HTMLAudioElement} = node.ownerDocument.defaultView; - return node instanceof HTMLAudioElement ? true : - node instanceof HTMLVideoElement ? isVideoLoadingAudio(node) : - false; -}; - -const isImage = ({namespaceURI, localName}) => - namespaceURI === HTML_NS && localName === "img" ? true : - namespaceURI === XUL_NS && localName === "image" ? true : - namespaceURI === SVG_NS && localName === "image" ? true : - false; - -parsers["reader/MediaType()"] = constant(node => - isImage(node) ? "image" : - isAudio(node) ? "audio" : - isVideo(node) ? "video" : - null); - - -const readLink = node => - node.namespaceURI === HTML_NS && node.localName === "a" ? node.href : - readLink(node.parentNode); - -parsers["reader/LinkURL()"] = constant(node => - node.matches("a, a *") ? readLink(node) : null); - -// Reader that reads out `true` if "contextmenu" `event.target` matches -// `descriptor.selector` and `false` if it does not. -parsers["reader/SelectorMatch()"] = ({selector}) => - node => node.matches(selector); - -// Accessing `selectionStart` and `selectionEnd` properties on non -// editable input nodes throw exceptions, there for we need this util -// function to guard us against them. -const getInputSelection = node => { - try { - if ("selectionStart" in node && "selectionEnd" in node) { - const {selectionStart, selectionEnd} = node; - return {selectionStart, selectionEnd} - } - } - catch(_) {} - - return null; -} - -// Selection reader does not really cares about descriptor so it is -// a constant function returning selection reader. Selection reader -// returns string of the selected text or `null` if there is no selection. -parsers["reader/Selection()"] = constant(node => { - const selection = node.ownerDocument.getSelection(); - if (!selection.isCollapsed) { - return selection.toString(); - } - // If target node is editable (text, input, textarea, etc..) document does - // not really handles selections there. There for we fallback to checking - // `selectionStart` `selectionEnd` properties and if they are present we - // extract selections manually from the `node.value`. - else { - const selection = getInputSelection(node); - const isSelected = selection && - Number.isInteger(selection.selectionStart) && - Number.isInteger(selection.selectionEnd) && - selection.selectionStart !== selection.selectionEnd; - return isSelected ? node.value.substring(selection.selectionStart, - selection.selectionEnd) : - null; - } -}); - -// Query reader just reads out properties from the node, so we just use `query` -// utility function. -parsers["reader/Query()"] = ({path}) => JSONReturn(query(path)); -// Attribute reader just reads attribute of the event target node. -parsers["reader/Attribute()"] = ({name}) => node => node.getAttribute(name); - -// Extractor reader defines generates a reader out of serialized function, who's -// return value is JSON cloned. Note: We do know source will evaluate to function -// as that's what we serialized on the other end, it's also ok if generated function -// is going to throw as registered readers are wrapped in try catch to avoid breakting -// unrelated readers. -parsers["reader/Extractor()"] = ({source}) => - JSONReturn(new Function("return (" + source + ")")()); - -// If the context-menu target node or any of its ancestors is one of these, -// Firefox uses a tailored context menu, and so the page context doesn't apply. -// There for `reader/isPage()` will read `false` in that case otherwise it's going -// to read `true`. -const nonPageElements = ["a", "applet", "area", "button", "canvas", "object", - "embed", "img", "input", "map", "video", "audio", "menu", - "option", "select", "textarea", "[contenteditable=true]"]; -const nonPageSelector = nonPageElements. - concat(nonPageElements.map(tag => `${tag} *`)). - join(", "); - -// Note: isPageContext implementation could have actually used SelectorMatch reader, -// but old implementation was also checked for collapsed selection there for to keep -// the behavior same we end up implementing a new reader. -parsers["reader/isPage()"] = constant(node => - node.ownerDocument.defaultView.getSelection().isCollapsed && - !node.matches(nonPageSelector)); - -// Reads `true` if node is in an iframe otherwise returns true. -parsers["reader/isFrame()"] = constant(node => - !!node.ownerDocument.defaultView.frameElement); - -parsers["reader/isEditable()"] = constant(node => { - const selection = getInputSelection(node); - return selection ? !node.readOnly && !node.disabled : node.isContentEditable; -}); - - -// TODO: Add some reader to read out tab id. - -const onReadersUpdate = message => { - each(([id, descriptor]) => { - if (descriptor) { - readers[id] = parse(descriptor); - } - else { - delete readers[id]; - } - }, pairs(message.data)); -}; -exports.onReadersUpdate = onReadersUpdate; - - -const onContextMenu = event => { - if (!event.defaultPrevented) { - const manager = nodeToMessageManager(event.target); - manager.sendSyncMessage("sdk/context-menu/read", read(event.target), readers); - } -}; -exports.onContextMenu = onContextMenu; - - -const onContentFrame = (frame) => { - // Listen for contextmenu events in on this frame. - frame.addEventListener("contextmenu", onContextMenu); - // Listen to registered reader changes and update registry. - frame.addMessageListener("sdk/context-menu/readers", onReadersUpdate); - - // Request table of readers (if this is loaded in a new process some table - // changes may be missed, this is way to sync up). - frame.sendAsyncMessage("sdk/context-menu/readers?"); -}; -exports.onContentFrame = onContentFrame; diff --git a/addon-sdk/source/lib/framescript/manager.js b/addon-sdk/source/lib/framescript/manager.js deleted file mode 100644 index 1f261e1fa..000000000 --- a/addon-sdk/source/lib/framescript/manager.js +++ /dev/null @@ -1,26 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -module.metadata = { - "stability": "unstable" -}; - -const mime = "application/javascript"; -const requireURI = module.uri.replace("framescript/manager.js", - "toolkit/require.js"); - -const requireLoadURI = `data:${mime},this["Components"].utils.import("${requireURI}")` - -// Loads module with given `id` into given `messageManager` via shared module loader. If `init` -// string is passed, will call module export with that name and pass frame script environment -// of the `messageManager` into it. Since module will load only once per process (which is -// once for chrome proces & second for content process) it is useful to have an init function -// to setup event listeners on each content frame. -const loadModule = (messageManager, id, allowDelayed, init) => { - const moduleLoadURI = `${requireLoadURI}.require("${id}")` - const uri = init ? `${moduleLoadURI}.${init}(this)` : moduleLoadURI; - messageManager.loadFrameScript(uri, allowDelayed); -}; -exports.loadModule = loadModule; diff --git a/addon-sdk/source/lib/framescript/util.js b/addon-sdk/source/lib/framescript/util.js deleted file mode 100644 index fb6834608..000000000 --- a/addon-sdk/source/lib/framescript/util.js +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -module.metadata = { - "stability": "unstable" -}; - - -const { Ci } = require("chrome"); - -const windowToMessageManager = window => - window. - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDocShell). - sameTypeRootTreeItem. - QueryInterface(Ci.nsIDocShell). - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIContentFrameMessageManager); -exports.windowToMessageManager = windowToMessageManager; - -const nodeToMessageManager = node => - windowToMessageManager(node.ownerDocument.defaultView); -exports.nodeToMessageManager = nodeToMessageManager; |