summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/framescript/content.jsm
blob: eaee26be3e2932fd97474a02e709be75356b875e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/* 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);
  }
}