addon-sdk/source/lib/sdk/page-worker.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 "use strict";
michael@0 5
michael@0 6 module.metadata = {
michael@0 7 "stability": "stable"
michael@0 8 };
michael@0 9
michael@0 10 const { Class } = require('./core/heritage');
michael@0 11 const { on, emit, off, setListeners } = require('./event/core');
michael@0 12 const { filter, pipe, map, merge: streamMerge, stripListeners } = require('./event/utils');
michael@0 13 const { detach, attach, destroy, WorkerHost } = require('./content/utils');
michael@0 14 const { Worker } = require('./content/worker');
michael@0 15 const { Disposable } = require('./core/disposable');
michael@0 16 const { WeakReference } = require('./core/reference');
michael@0 17 const { EventTarget } = require('./event/target');
michael@0 18 const { unload } = require('./system/unload');
michael@0 19 const { events, streamEventsFrom } = require('./content/events');
michael@0 20 const { getAttachEventType } = require('./content/utils');
michael@0 21 const { window } = require('./addon/window');
michael@0 22 const { getParentWindow } = require('./window/utils');
michael@0 23 const { create: makeFrame, getDocShell } = require('./frame/utils');
michael@0 24 const { contract } = require('./util/contract');
michael@0 25 const { contract: loaderContract } = require('./content/loader');
michael@0 26 const { has } = require('./util/array');
michael@0 27 const { Rules } = require('./util/rules');
michael@0 28 const { merge } = require('./util/object');
michael@0 29
michael@0 30 const views = WeakMap();
michael@0 31 const workers = WeakMap();
michael@0 32 const pages = WeakMap();
michael@0 33
michael@0 34 const readyEventNames = [
michael@0 35 'DOMContentLoaded',
michael@0 36 'document-element-inserted',
michael@0 37 'load'
michael@0 38 ];
michael@0 39
michael@0 40 function workerFor(page) workers.get(page)
michael@0 41 function pageFor(view) pages.get(view)
michael@0 42 function viewFor(page) views.get(page)
michael@0 43 function isDisposed (page) !views.get(page, false)
michael@0 44
michael@0 45 let pageContract = contract(merge({
michael@0 46 allow: {
michael@0 47 is: ['object', 'undefined', 'null'],
michael@0 48 map: function (allow) { return { script: !allow || allow.script !== false }}
michael@0 49 },
michael@0 50 onMessage: {
michael@0 51 is: ['function', 'undefined']
michael@0 52 },
michael@0 53 include: {
michael@0 54 is: ['string', 'array', 'undefined']
michael@0 55 },
michael@0 56 contentScriptWhen: {
michael@0 57 is: ['string', 'undefined']
michael@0 58 }
michael@0 59 }, loaderContract.rules));
michael@0 60
michael@0 61 function enableScript (page) {
michael@0 62 getDocShell(viewFor(page)).allowJavascript = true;
michael@0 63 }
michael@0 64
michael@0 65 function disableScript (page) {
michael@0 66 getDocShell(viewFor(page)).allowJavascript = false;
michael@0 67 }
michael@0 68
michael@0 69 function Allow (page) {
michael@0 70 return {
michael@0 71 get script() { return getDocShell(viewFor(page)).allowJavascript; },
michael@0 72 set script(value) { return value ? enableScript(page) : disableScript(page); }
michael@0 73 };
michael@0 74 }
michael@0 75
michael@0 76 function injectWorker ({page}) {
michael@0 77 let worker = workerFor(page);
michael@0 78 let view = viewFor(page);
michael@0 79 if (isValidURL(page, view.contentDocument.URL))
michael@0 80 attach(worker, view.contentWindow);
michael@0 81 }
michael@0 82
michael@0 83 function isValidURL(page, url) !page.rules || page.rules.matchesAny(url)
michael@0 84
michael@0 85 const Page = Class({
michael@0 86 implements: [
michael@0 87 EventTarget,
michael@0 88 Disposable,
michael@0 89 WeakReference
michael@0 90 ],
michael@0 91 extends: WorkerHost(workerFor),
michael@0 92 setup: function Page(options) {
michael@0 93 let page = this;
michael@0 94 options = pageContract(options);
michael@0 95 let view = makeFrame(window.document, {
michael@0 96 nodeName: 'iframe',
michael@0 97 type: 'content',
michael@0 98 uri: options.contentURL,
michael@0 99 allowJavascript: options.allow.script,
michael@0 100 allowPlugins: true,
michael@0 101 allowAuth: true
michael@0 102 });
michael@0 103
michael@0 104 ['contentScriptFile', 'contentScript', 'contentScriptWhen']
michael@0 105 .forEach(prop => page[prop] = options[prop]);
michael@0 106
michael@0 107 views.set(this, view);
michael@0 108 pages.set(view, this);
michael@0 109
michael@0 110 // Set listeners on the {Page} object itself, not the underlying worker,
michael@0 111 // like `onMessage`, as it gets piped
michael@0 112 setListeners(this, options);
michael@0 113 let worker = new Worker(stripListeners(options));
michael@0 114 workers.set(this, worker);
michael@0 115 pipe(worker, this);
michael@0 116
michael@0 117 if (this.include || options.include) {
michael@0 118 this.rules = Rules();
michael@0 119 this.rules.add.apply(this.rules, [].concat(this.include || options.include));
michael@0 120 }
michael@0 121 },
michael@0 122 get allow() { return Allow(this); },
michael@0 123 set allow(value) {
michael@0 124 let allowJavascript = pageContract({ allow: value }).allow.script;
michael@0 125 return allowJavascript ? enableScript(this) : disableScript(this);
michael@0 126 },
michael@0 127 get contentURL() { return viewFor(this).getAttribute('src'); },
michael@0 128 set contentURL(value) {
michael@0 129 if (!isValidURL(this, value)) return;
michael@0 130 let view = viewFor(this);
michael@0 131 let contentURL = pageContract({ contentURL: value }).contentURL;
michael@0 132 view.setAttribute('src', contentURL);
michael@0 133 },
michael@0 134 dispose: function () {
michael@0 135 if (isDisposed(this)) return;
michael@0 136 let view = viewFor(this);
michael@0 137 if (view.parentNode) view.parentNode.removeChild(view);
michael@0 138 views.delete(this);
michael@0 139 destroy(workers.get(this));
michael@0 140 },
michael@0 141 toString: function () { return '[object Page]' }
michael@0 142 });
michael@0 143
michael@0 144 exports.Page = Page;
michael@0 145
michael@0 146 let pageEvents = streamMerge([events, streamEventsFrom(window)]);
michael@0 147 let readyEvents = filter(pageEvents, isReadyEvent);
michael@0 148 let formattedEvents = map(readyEvents, function({target, type}) {
michael@0 149 return { type: type, page: pageFromDoc(target) };
michael@0 150 });
michael@0 151 let pageReadyEvents = filter(formattedEvents, function({page, type}) {
michael@0 152 return getAttachEventType(page) === type});
michael@0 153 on(pageReadyEvents, 'data', injectWorker);
michael@0 154
michael@0 155 function isReadyEvent ({type}) {
michael@0 156 return has(readyEventNames, type);
michael@0 157 }
michael@0 158
michael@0 159 /*
michael@0 160 * Takes a document, finds its doc shell tree root and returns the
michael@0 161 * matching Page instance if found
michael@0 162 */
michael@0 163 function pageFromDoc(doc) {
michael@0 164 let parentWindow = getParentWindow(doc.defaultView), page;
michael@0 165 if (!parentWindow) return;
michael@0 166
michael@0 167 let frames = parentWindow.document.getElementsByTagName('iframe');
michael@0 168 for (let i = frames.length; i--;)
michael@0 169 if (frames[i].contentDocument === doc && (page = pageFor(frames[i])))
michael@0 170 return page;
michael@0 171 return null;
michael@0 172 }

mercurial