summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
blob: 32299fb04e1dec9ff437c890ce4a79da0f0f7e69 (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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
"use strict";

const { Schemas } = ChromeUtils.importESModule(
  "resource://gre/modules/Schemas.sys.mjs"
);

const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";

const CATEGORY_EXTENSION_MODULES = "webextension-modules";
const CATEGORY_EXTENSION_SCHEMAS = "webextension-schemas";
const CATEGORY_EXTENSION_SCRIPTS = "webextension-scripts";

const CATEGORY_EXTENSION_SCRIPTS_ADDON = "webextension-scripts-addon";
const CATEGORY_EXTENSION_SCRIPTS_CONTENT = "webextension-scripts-content";
const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";

let schemaURLs = new Set();
schemaURLs.add("chrome://extensions/content/schemas/experiments.json");

// Helper class used to load the API modules similarly to the apiManager
// defined in ExtensionParent.jsm.
class FakeAPIManager extends ExtensionCommon.SchemaAPIManager {
  constructor(processType = "main") {
    super(processType, Schemas);
    this.initialized = false;
  }

  getModuleJSONURLs() {
    return Array.from(
      Services.catMan.enumerateCategory(CATEGORY_EXTENSION_MODULES),
      ({ value }) => value
    );
  }

  async lazyInit() {
    if (this.initialized) {
      return;
    }

    this.initialized = true;

    let modulesPromise = this.loadModuleJSON(this.getModuleJSONURLs());

    let scriptURLs = [];
    for (let { value } of Services.catMan.enumerateCategory(
      CATEGORY_EXTENSION_SCRIPTS
    )) {
      scriptURLs.push(value);
    }

    let scripts = await Promise.all(
      scriptURLs.map(url => ChromeUtils.compileScript(url))
    );

    this.initModuleData(await modulesPromise);

    this.initGlobal();
    for (let script of scripts) {
      script.executeInGlobal(this.global);
    }

    // Load order matters here. The base manifest defines types which are
    // extended by other schemas, so needs to be loaded first.
    await Schemas.load(BASE_SCHEMA).then(() => {
      let promises = [];
      for (let { value } of Services.catMan.enumerateCategory(
        CATEGORY_EXTENSION_SCHEMAS
      )) {
        promises.push(Schemas.load(value));
      }
      for (let [url, { content }] of this.schemaURLs) {
        promises.push(Schemas.load(url, content));
      }
      for (let url of schemaURLs) {
        promises.push(Schemas.load(url));
      }
      return Promise.all(promises).then(() => {
        Schemas.updateSharedSchemas();
      });
    });
  }

  async loadAllModules(reverseOrder = false) {
    await this.lazyInit();

    let apiModuleNames = Array.from(this.modules.keys())
      .filter(moduleName => {
        let moduleDesc = this.modules.get(moduleName);
        return moduleDesc && !!moduleDesc.url;
      })
      .sort();

    apiModuleNames = reverseOrder ? apiModuleNames.reverse() : apiModuleNames;

    for (let apiModule of apiModuleNames) {
      info(
        `Loading apiModule ${apiModule}: ${this.modules.get(apiModule).url}`
      );
      await this.asyncLoadModule(apiModule);
    }
  }
}

// Specialized helper class used to test loading "child process" modules (similarly to the
// SchemaAPIManagers sub-classes defined in ExtensionPageChild.jsm and ExtensionContent.jsm).
class FakeChildProcessAPIManager extends FakeAPIManager {
  constructor({ processType, categoryScripts }) {
    super(processType, Schemas);

    this.categoryScripts = categoryScripts;
  }

  async lazyInit() {
    if (!this.initialized) {
      this.initialized = true;
      this.initGlobal();
      for (let { value } of Services.catMan.enumerateCategory(
        this.categoryScripts
      )) {
        await this.loadScript(value);
      }
    }
  }
}

async function test_loading_api_modules(createAPIManager) {
  let fakeAPIManager;

  info("Load API modules in alphabetic order");

  fakeAPIManager = createAPIManager();
  await fakeAPIManager.loadAllModules();

  info("Load API modules in reverse order");

  fakeAPIManager = createAPIManager();
  await fakeAPIManager.loadAllModules(true);
}

add_task(function test_loading_main_process_api_modules() {
  return test_loading_api_modules(() => {
    return new FakeAPIManager();
  });
});

add_task(function test_loading_extension_process_modules() {
  return test_loading_api_modules(() => {
    return new FakeChildProcessAPIManager({
      processType: "addon",
      categoryScripts: CATEGORY_EXTENSION_SCRIPTS_ADDON,
    });
  });
});

add_task(function test_loading_devtools_modules() {
  return test_loading_api_modules(() => {
    return new FakeChildProcessAPIManager({
      processType: "devtools",
      categoryScripts: CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS,
    });
  });
});

add_task(async function test_loading_content_process_modules() {
  return test_loading_api_modules(() => {
    return new FakeChildProcessAPIManager({
      processType: "content",
      categoryScripts: CATEGORY_EXTENSION_SCRIPTS_CONTENT,
    });
  });
});