mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-10 10:18:38 +09:00
Move Add-on SDK source to toolkit/jetpack
This commit is contained in:
parent
0a03067ee3
commit
23b67db438
993 changed files with 484 additions and 100663 deletions
|
|
@ -1,288 +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/. */
|
||||
|
||||
/**
|
||||
*
|
||||
* `deprecated/sync-worker` was previously `content/worker`, that was
|
||||
* incompatible with e10s. we are in the process of switching to the new
|
||||
* asynchronous `Worker`, which behaves slightly differently in some edge
|
||||
* cases, so we are keeping this one around for a short period.
|
||||
* try to switch to the new one as soon as possible..
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { Class } = require('../core/heritage');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { on, off, emit, setListeners } = require('../event/core');
|
||||
const {
|
||||
attach, detach, destroy
|
||||
} = require('../content/utils');
|
||||
const { method } = require('../lang/functional');
|
||||
const { Ci, Cu, Cc } = require('chrome');
|
||||
const unload = require('../system/unload');
|
||||
const events = require('../system/events');
|
||||
const { getInnerId } = require("../window/utils");
|
||||
const { WorkerSandbox } = require('../content/sandbox');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
|
||||
// A weak map of workers to hold private attributes that
|
||||
// should not be exposed
|
||||
const workers = new WeakMap();
|
||||
|
||||
var modelFor = (worker) => workers.get(worker);
|
||||
|
||||
const ERR_DESTROYED =
|
||||
"Couldn't find the worker to receive this message. " +
|
||||
"The script may not be initialized yet, or may already have been unloaded.";
|
||||
|
||||
const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
|
||||
"until it is visible again.";
|
||||
|
||||
/**
|
||||
* Message-passing facility for communication between code running
|
||||
* in the content and add-on process.
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
|
||||
*/
|
||||
const Worker = Class({
|
||||
implements: [EventTarget],
|
||||
initialize: function WorkerConstructor (options) {
|
||||
// Save model in weak map to not expose properties
|
||||
let model = createModel();
|
||||
workers.set(this, model);
|
||||
|
||||
options = options || {};
|
||||
|
||||
if ('contentScriptFile' in options)
|
||||
this.contentScriptFile = options.contentScriptFile;
|
||||
if ('contentScriptOptions' in options)
|
||||
this.contentScriptOptions = options.contentScriptOptions;
|
||||
if ('contentScript' in options)
|
||||
this.contentScript = options.contentScript;
|
||||
if ('injectInDocument' in options)
|
||||
this.injectInDocument = !!options.injectInDocument;
|
||||
|
||||
setListeners(this, options);
|
||||
|
||||
unload.ensure(this, "destroy");
|
||||
|
||||
// Ensure that worker.port is initialized for contentWorker to be able
|
||||
// to send events during worker initialization.
|
||||
this.port = createPort(this);
|
||||
|
||||
model.documentUnload = documentUnload.bind(this);
|
||||
model.pageShow = pageShow.bind(this);
|
||||
model.pageHide = pageHide.bind(this);
|
||||
|
||||
if ('window' in options)
|
||||
attach(this, options.window);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message to the worker's global scope. Method takes single
|
||||
* argument, which represents data to be sent to the worker. The data may
|
||||
* be any primitive type value or `JSON`. Call of this method asynchronously
|
||||
* emits `message` event with data value in the global scope of this
|
||||
* worker.
|
||||
*
|
||||
* `message` event listeners can be set either by calling
|
||||
* `self.on` with a first argument string `"message"` or by
|
||||
* implementing `onMessage` function in the global scope of this worker.
|
||||
* @param {Number|String|JSON} data
|
||||
*/
|
||||
postMessage: function (...data) {
|
||||
let model = modelFor(this);
|
||||
let args = ['message'].concat(data);
|
||||
if (!model.inited) {
|
||||
model.earlyEvents.push(args);
|
||||
return;
|
||||
}
|
||||
processMessage.apply(null, [this].concat(args));
|
||||
},
|
||||
|
||||
get url () {
|
||||
let model = modelFor(this);
|
||||
// model.window will be null after detach
|
||||
return model.window ? model.window.document.location.href : null;
|
||||
},
|
||||
|
||||
get contentURL () {
|
||||
let model = modelFor(this);
|
||||
return model.window ? model.window.document.URL : null;
|
||||
},
|
||||
|
||||
// Implemented to provide some of the previous features of exposing sandbox
|
||||
// so that Worker can be extended
|
||||
getSandbox: function () {
|
||||
return modelFor(this).contentWorker;
|
||||
},
|
||||
|
||||
toString: function () { return '[object Worker]'; },
|
||||
attach: method(attach),
|
||||
detach: method(detach),
|
||||
destroy: method(destroy)
|
||||
});
|
||||
exports.Worker = Worker;
|
||||
|
||||
attach.define(Worker, function (worker, window) {
|
||||
let model = modelFor(worker);
|
||||
model.window = window;
|
||||
// Track document unload to destroy this worker.
|
||||
// We can't watch for unload event on page's window object as it
|
||||
// prevents bfcache from working:
|
||||
// https://developer.mozilla.org/En/Working_with_BFCache
|
||||
model.windowID = getInnerId(model.window);
|
||||
events.on("inner-window-destroyed", model.documentUnload);
|
||||
|
||||
// will set model.contentWorker pointing to the private API:
|
||||
model.contentWorker = WorkerSandbox(worker, model.window);
|
||||
|
||||
// Listen to pagehide event in order to freeze the content script
|
||||
// while the document is frozen in bfcache:
|
||||
model.window.addEventListener("pageshow", model.pageShow, true);
|
||||
model.window.addEventListener("pagehide", model.pageHide, true);
|
||||
|
||||
// Mainly enable worker.port.emit to send event to the content worker
|
||||
model.inited = true;
|
||||
model.frozen = false;
|
||||
|
||||
// Fire off `attach` event
|
||||
emit(worker, 'attach', window);
|
||||
|
||||
// Process all events and messages that were fired before the
|
||||
// worker was initialized.
|
||||
model.earlyEvents.forEach(args => processMessage.apply(null, [worker].concat(args)));
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove all internal references to the attached document
|
||||
* Tells _port to unload itself and removes all the references from itself.
|
||||
*/
|
||||
detach.define(Worker, function (worker, reason) {
|
||||
let model = modelFor(worker);
|
||||
|
||||
// maybe unloaded before content side is created
|
||||
if (model.contentWorker) {
|
||||
model.contentWorker.destroy(reason);
|
||||
}
|
||||
|
||||
model.contentWorker = null;
|
||||
if (model.window) {
|
||||
model.window.removeEventListener("pageshow", model.pageShow, true);
|
||||
model.window.removeEventListener("pagehide", model.pageHide, true);
|
||||
}
|
||||
model.window = null;
|
||||
// This method may be called multiple times,
|
||||
// avoid dispatching `detach` event more than once
|
||||
if (model.windowID) {
|
||||
model.windowID = null;
|
||||
events.off("inner-window-destroyed", model.documentUnload);
|
||||
model.earlyEvents.length = 0;
|
||||
emit(worker, 'detach');
|
||||
}
|
||||
model.inited = false;
|
||||
});
|
||||
|
||||
isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
|
||||
|
||||
/**
|
||||
* Tells content worker to unload itself and
|
||||
* removes all the references from itself.
|
||||
*/
|
||||
destroy.define(Worker, function (worker, reason) {
|
||||
detach(worker, reason);
|
||||
modelFor(worker).inited = true;
|
||||
// Specifying no type or listener removes all listeners
|
||||
// from target
|
||||
off(worker);
|
||||
off(worker.port);
|
||||
});
|
||||
|
||||
/**
|
||||
* Events fired by workers
|
||||
*/
|
||||
function documentUnload ({ subject, data }) {
|
||||
let model = modelFor(this);
|
||||
let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (innerWinID != model.windowID) return false;
|
||||
detach(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
function pageShow () {
|
||||
let model = modelFor(this);
|
||||
model.contentWorker.emitSync('pageshow');
|
||||
emit(this, 'pageshow');
|
||||
model.frozen = false;
|
||||
}
|
||||
|
||||
function pageHide () {
|
||||
let model = modelFor(this);
|
||||
model.contentWorker.emitSync('pagehide');
|
||||
emit(this, 'pagehide');
|
||||
model.frozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired from postMessage and emitEventToContent, or from the earlyMessage
|
||||
* queue when fired before the content is loaded. Sends arguments to
|
||||
* contentWorker if able
|
||||
*/
|
||||
|
||||
function processMessage (worker, ...args) {
|
||||
let model = modelFor(worker) || {};
|
||||
if (!model.contentWorker)
|
||||
throw new Error(ERR_DESTROYED);
|
||||
if (model.frozen)
|
||||
throw new Error(ERR_FROZEN);
|
||||
model.contentWorker.emit.apply(null, args);
|
||||
}
|
||||
|
||||
function createModel () {
|
||||
return {
|
||||
// List of messages fired before worker is initialized
|
||||
earlyEvents: [],
|
||||
// Is worker connected to the content worker sandbox ?
|
||||
inited: false,
|
||||
// Is worker being frozen? i.e related document is frozen in bfcache.
|
||||
// Content script should not be reachable if frozen.
|
||||
frozen: true,
|
||||
/**
|
||||
* Reference to the content side of the worker.
|
||||
* @type {WorkerGlobalScope}
|
||||
*/
|
||||
contentWorker: null,
|
||||
/**
|
||||
* Reference to the window that is accessible from
|
||||
* the content scripts.
|
||||
* @type {Object}
|
||||
*/
|
||||
window: null
|
||||
};
|
||||
}
|
||||
|
||||
function createPort (worker) {
|
||||
let port = EventTarget();
|
||||
port.emit = emitEventToContent.bind(null, worker);
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a custom event to the content script,
|
||||
* i.e. emit this event on `self.port`
|
||||
*/
|
||||
function emitEventToContent (worker, ...eventArgs) {
|
||||
let model = modelFor(worker);
|
||||
let args = ['event'].concat(eventArgs);
|
||||
if (!model.inited) {
|
||||
model.earlyEvents.push(args);
|
||||
return;
|
||||
}
|
||||
processMessage.apply(null, [worker].concat(args));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue