Sat, 03 Jan 2015 20:18:00 +0100
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 | } |