/** * Instruments a function on the specified target object. */ function overrideFunction(global, target, name, descriptor, callback) { // Invoking .apply on an unxrayed content function doesn't work, because // the arguments array is inaccessible to it. Get Xrays back. let originalFunc = Cu.unwaiveXrays(target[name]); Cu.exportFunction(function(...args) { let result; try { result = Cu.waiveXrays(originalFunc.apply(this, args)); } catch (e) { throw createContentError(e, unwrappedWindow); } if (self._recording) { let type = CallWatcherFront.METHOD_FUNCTION; let stack = getStack(name); let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch; callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result); } return result; }, target, { defineAs: name }); Object.defineProperty(target, name, { configurable: descriptor.configurable, enumerable: descriptor.enumerable, writable: true }); }
onReady: function(options) { BaseSidePanel.prototype.onReady.apply(this, arguments); Trace.sysout("commandEditor.onReady"); this.window = XPCNativeWrapper.unwrap(options.window); // Setup communication channel with the frame (type == content). // The content (panel frame) can send messages to this panel object // through 'sendMessage' method. The message are handled by // 'onMessage' below. Cu.exportFunction(this.onMessage, this.window, { defineAs: "sendMessage" }); },
function exportIntoContentScope(win, obj, defineAs) { let clone = Cu.createObjectIn(win, { defineAs: defineAs }); let props = Object.getOwnPropertyNames(obj); for (let i = 0; i < props.length; i++) { let propName = props[i]; let propValue = obj[propName]; if (typeof propValue == "function") { Cu.exportFunction(propValue, clone, { defineAs: propName }); } } }
Events.once(win, "DOMContentLoaded", event => { Cu.exportFunction(this.postChromeMessage.bind(this), win, { defineAs: "postChromeMessage" }); })
initialize: function WorkerSandbox(worker, window) { let model = {}; sandboxes.set(this, model); model.worker = worker; // We receive a wrapped window, that may be an xraywrapper if it's content let proto = window; // TODO necessary? // Ensure that `emit` has always the right `this` this.emit = this.emit.bind(this); this.emitSync = this.emitSync.bind(this); // Use expanded principal for content-script if the content is a // regular web content for better isolation. // (This behavior can be turned off for now with the unsafe-content-script // flag to give addon developers time for making the necessary changes) // But prevent it when the Worker isn't used for a content script but for // injecting `addon` object into a Panel scope, for example. // That's because: // 1/ It is useless to use multiple domains as the worker is only used // to communicate with the addon, // 2/ By using it it would prevent the document to have access to any JS // value of the worker. As JS values coming from multiple domain principals // can't be accessed by 'mono-principals' (principal with only one domain). // Even if this principal is for a domain that is specified in the multiple // domain principal. let principals = window; let wantGlobalProperties = []; let isSystemPrincipal = secMan.isSystemPrincipal( window.document.nodePrincipal); if (!isSystemPrincipal && !requiresAddonGlobal(worker)) { if (EXPANDED_PRINCIPALS.length > 0) { // We have to replace XHR constructor of the content document // with a custom cross origin one, automagically added by platform code: delete proto.XMLHttpRequest; wantGlobalProperties.push('XMLHttpRequest'); } if (!waiveSecurityMembrane) principals = EXPANDED_PRINCIPALS.concat(window); } // Create the sandbox and bind it to window in order for content scripts to // have access to all standard globals (window, document, ...) let content = sandbox(principals, { sandboxPrototype: proto, wantXrays: !requiresAddonGlobal(worker), wantGlobalProperties: wantGlobalProperties, wantExportHelpers: true, sameZoneAs: window, metadata: { SDKContentScript: true, 'inner-window-id': getInnerId(window) } }); model.sandbox = content; // We have to ensure that window.top and window.parent are the exact same // object than window object, i.e. the sandbox global object. But not // always, in case of iframes, top and parent are another window object. let top = window.top === window ? content : content.top; let parent = window.parent === window ? content : content.parent; merge(content, { // We need 'this === window === top' to be true in toplevel scope: get window() { return content; }, get top() { return top; }, get parent() { return parent; } }); // Use the Greasemonkey naming convention to provide access to the // unwrapped window object so the content script can access document // JavaScript values. // NOTE: this functionality is experimental and may change or go away // at any time! // // Note that because waivers aren't propagated between origins, we // need the unsafeWindow getter to live in the sandbox. var unsafeWindowGetter = new content.Function('return window.wrappedJSObject || window;'); Object.defineProperty(content, 'unsafeWindow', {get: unsafeWindowGetter}); // Load trusted code that will inject content script API. let ContentWorker = load(content, CONTENT_WORKER_URL); // prepare a clean `self.options` let options = 'contentScriptOptions' in worker ? JSON.stringify(worker.contentScriptOptions) : undefined; // Then call `inject` method and communicate with this script // by trading two methods that allow to send events to the other side: // - `onEvent` called by content script // - `result.emitToContent` called by addon script let onEvent = Cu.exportFunction(onContentEvent.bind(null, this), ContentWorker); let chromeAPI = createChromeAPI(ContentWorker); let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options); // Merge `emitToContent` into our private model of the // WorkerSandbox so we can communicate with content script model.emitToContent = result; let console = new PlainTextConsole(null, getInnerId(window)); // Handle messages send by this script: setListeners(this, console); // Inject `addon` global into target document if document is trusted, // `addon` in document is equivalent to `self` in content script. if (requiresAddonGlobal(worker)) { Object.defineProperty(getUnsafeWindow(window), 'addon', { value: content.self, configurable: true } ); } // Inject our `console` into target document if worker doesn't have a tab // (e.g Panel, PageWorker). // `worker.tab` can't be used because bug 804935. if (!isWindowInTab(window)) { let win = getUnsafeWindow(window); // export our chrome console to content window, as described here: // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn let con = Cu.createObjectIn(win); let genPropDesc = function genPropDesc(fun) { return { enumerable: true, configurable: true, writable: true, value: console[fun] }; } const properties = { log: genPropDesc('log'), info: genPropDesc('info'), warn: genPropDesc('warn'), error: genPropDesc('error'), debug: genPropDesc('debug'), trace: genPropDesc('trace'), dir: genPropDesc('dir'), group: genPropDesc('group'), groupCollapsed: genPropDesc('groupCollapsed'), groupEnd: genPropDesc('groupEnd'), time: genPropDesc('time'), timeEnd: genPropDesc('timeEnd'), profile: genPropDesc('profile'), profileEnd: genPropDesc('profileEnd'), exception: genPropDesc('exception'), assert: genPropDesc('assert'), count: genPropDesc('count'), table: genPropDesc('table'), clear: genPropDesc('clear'), dirxml: genPropDesc('dirxml'), markTimeline: genPropDesc('markTimeline'), timeline: genPropDesc('timeline'), timelineEnd: genPropDesc('timelineEnd'), timeStamp: genPropDesc('timeStamp'), }; Object.defineProperties(con, properties); Cu.makeObjectPropsNormal(con); win.console = con; }; emit(events, "content-script-before-inserted", { window: window, worker: worker }); // The order of `contentScriptFile` and `contentScript` evaluation is // intentional, so programs can load libraries like jQuery from script URLs // and use them in scripts. let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile : null, contentScript = ('contentScript' in worker) ? worker.contentScript : null; if (contentScriptFile) importScripts.apply(null, [this].concat(contentScriptFile)); if (contentScript) { evaluateIn( this, Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript ); } },
function injectFunctions(event) { // event.subject is an nsIDOMWindow // event.data is a string representing the origin Debug.log("injecting functions for origin " + event.data); let domWindow = event.subject; // Add to window all the functions we want user-side code to be able to call. Cu.exportFunction(RiverTrailInterface.riverTrailExtensionIsInstalled, domWindow, {defineAs: "riverTrailExtensionIsInstalled"}); Cu.exportFunction(RiverTrailInterface.is64BitFloatingPointEnabled, domWindow, {defineAs: "is64BitFloatingPointEnabled"}); Cu.exportFunction(RiverTrailInterface.initContext, domWindow, {defineAs: "initContext"}); Cu.exportFunction(RiverTrailInterface.canBeMapped, domWindow, {defineAs: "canBeMapped"}); Cu.exportFunction(RiverTrailInterface.compileKernel, domWindow, {defineAs: "compileKernel"}); Cu.exportFunction(RiverTrailInterface.getBuildLog, domWindow, {defineAs: "getBuildLog"}); Cu.exportFunction(RiverTrailInterface.mapData, domWindow, {defineAs: "mapData"}); Cu.exportFunction(RiverTrailInterface.setArgument, domWindow, {defineAs: "setArgument"}); Cu.exportFunction(RiverTrailInterface.setScalarArgument, domWindow, {defineAs: "setScalarArgument"}); Cu.exportFunction(RiverTrailInterface.run, domWindow, {defineAs: "run"}); Cu.exportFunction(RiverTrailInterface.getValue, domWindow, {defineAs: "getValue", allowCallbacks: true}); Debug.log("finished injecting functions"); }