Skip to content

Commit

Permalink
Bug 1853013 - Ship 118.3.0 of feature-webcompat
Browse files Browse the repository at this point in the history
  • Loading branch information
wisniewskit authored and mergify[bot] committed Sep 18, 2023
1 parent 3de0f39 commit 4fe5f5e
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 78 deletions.
Expand Up @@ -4,7 +4,7 @@

"use strict";

/* global ExtensionAPI, ExtensionCommon, Services, XPCOMUtils */
/* global ExtensionAPI, ExtensionCommon, Services */

this.aboutConfigPrefs = class extends ExtensionAPI {
getAPI(context) {
Expand Down
Expand Up @@ -49,6 +49,28 @@
],
"async": true
},
{
"name": "getBoolPrefSync",
"type": "function",
"description": "Get a webcompat preference's boolean value synchronously",
"parameters": [
{
"name": "name",
"type": "string",
"description": "The preference name"
},
{
"name": "defaultValue",
"type": "boolean",
"optional": true,
"description": "Default value to return if the pref is not set (defaults to false if omitted)"
}
],
"returns": {
"type": "boolean",
"description": "returns the value of a boolean pref."
}
},
{
"name": "setPref",
"type": "function",
Expand Down
@@ -0,0 +1,29 @@
/* 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";

/* global ExtensionAPI, Services, XPCOMUtils */

this.aboutConfigPrefs = class extends ExtensionAPI {
getAPI(context) {
const extensionIDBase = context.extension.id.split("@")[0];
const extensionPrefNameBase = `extensions.${extensionIDBase}.`;

return {
aboutConfigPrefs: {
getBoolPrefSync(prefName, defaultValue = false) {
try {
return Services.prefs.getBoolPref(
`${extensionPrefNameBase}${prefName}`,
defaultValue
);
} catch (_) {
return defaultValue;
}
},
},
};
}
};
Expand Up @@ -14,7 +14,24 @@ class Injections {

this._availableInjections = availableInjections;
this._activeInjections = new Set();
// Only used if this.shouldUseScriptingAPI is false and we are falling back
// to use the contentScripts API.
this._activeInjectionHandles = new Map();
this._customFunctions = customFunctions;

this.shouldUseScriptingAPI =
browser.aboutConfigPrefs.getBoolPrefSync("useScriptingAPI");
// Debug log emit only on nightly (similarly to the debug
// helper used in shims.js for similar purpose).
browser.appConstants.getReleaseBranch().then(releaseBranch => {
if (releaseBranch !== "release_or_beta") {
console.debug(
`WebCompat Injections will be injected using ${
this.shouldUseScriptingAPI ? "scripting" : "contentScripts"
} API`
);
}
});
}

bindAboutCompatBroker(broker) {
Expand Down Expand Up @@ -83,7 +100,9 @@ class Injections {
platformInfo.os == "android" ? "android" : "desktop",
];

let registeredScriptIds = await this.getPromiseRegisteredScriptIds();
let registeredScriptIds = this.shouldUseScriptingAPI
? await this.getPromiseRegisteredScriptIds()
: [];

for (const injection of this._availableInjections) {
if (platformMatches.includes(injection.platform)) {
Expand All @@ -103,30 +122,33 @@ class Injections {
buildContentScriptRegistrations(contentScripts) {
let finalConfig = Object.assign({}, contentScripts);

// Don't persist the content scripts across browser restarts
// (at least not yet, we would need to apply some more changes
// to adjust webcompat for accounting for the scripts to be
// already registered).
//
// NOTE: scripting API has been introduced in Gecko 102,
// prior to Gecko 105 persistAcrossSessions option was required
// and only accepted false persistAcrossSessions, after Gecko 105
// is optional and defaults to true.
finalConfig.persistAcrossSessions = false;

if (!finalConfig.runAt) {
finalConfig.runAt = "document_start";
}

// Convert js/css from contentScripts.register API method
// format to scripting.registerContentScripts API method
// format.
if (Array.isArray(finalConfig.js)) {
finalConfig.js = finalConfig.js.map(e => e.file);
}
if (this.shouldUseScriptingAPI) {
// Don't persist the content scripts across browser restarts
// (at least not yet, we would need to apply some more changes
// to adjust webcompat for accounting for the scripts to be
// already registered).
//
// NOTE: scripting API has been introduced in Gecko 102,
// prior to Gecko 105 persistAcrossSessions option was required
// and only accepted false persistAcrossSessions, after Gecko 105
// is optional and defaults to true.

finalConfig.persistAcrossSessions = false;

// Convert js/css from contentScripts.register API method
// format to scripting.registerContentScripts API method
// format.
if (Array.isArray(finalConfig.js)) {
finalConfig.js = finalConfig.js.map(e => e.file);
}

if (Array.isArray(finalConfig.css)) {
finalConfig.css = finalConfig.css.map(e => e.file);
if (Array.isArray(finalConfig.css)) {
finalConfig.css = finalConfig.css.map(e => e.file);
}
}

return finalConfig;
Expand Down Expand Up @@ -159,22 +181,31 @@ class Injections {
let injectProps;
try {
const { id } = injection;
// enableContentScripts receives a registeredScriptIds already
// pre-computed once from registerContentScripts to register all
// the injection, whereas it does not expect to receive one when
// it is called from the AboutCompatBroker to re-enable one specific
// injection.
let activeScriptIds = Array.isArray(registeredScriptIds)
? registeredScriptIds
: await this.getPromiseRegisteredScriptIds([id]);
injectProps = this.buildContentScriptRegistrations(
injection.contentScripts
);
injectProps.id = id;
if (!activeScriptIds.includes(id)) {
await browser.scripting.registerContentScripts([injectProps]);
if (this.shouldUseScriptingAPI) {
// enableContentScripts receives a registeredScriptIds already
// pre-computed once from registerContentScripts to register all
// the injection, whereas it does not expect to receive one when
// it is called from the AboutCompatBroker to re-enable one specific
// injection.
let activeScriptIds = Array.isArray(registeredScriptIds)
? registeredScriptIds
: await this.getPromiseRegisteredScriptIds([id]);
injectProps = this.buildContentScriptRegistrations(
injection.contentScripts
);
injectProps.id = id;
if (!activeScriptIds.includes(id)) {
await browser.scripting.registerContentScripts([injectProps]);
}
this._activeInjections.add(id);
} else {
const handle = await browser.contentScripts.register(
this.buildContentScriptRegistrations(injection.contentScripts)
);
this._activeInjections.add(id);
this._activeInjectionHandles.set(id, handle);
}
this._activeInjections.add(id);

injection.active = true;
} catch (ex) {
console.error(
Expand Down Expand Up @@ -223,7 +254,15 @@ class Injections {

async disableContentScripts(injection) {
if (this._activeInjections.has(injection.id)) {
await browser.scripting.unregisterContentScripts({ ids: [injection.id] });
if (this.shouldUseScriptingAPI) {
await browser.scripting.unregisterContentScripts({
ids: [injection.id],
});
} else {
const handle = this._activeInjectionHandles.get(injection.id);
await handle.unregister();
this._activeInjectionHandles.delete(injection.id);
}
this._activeInjections.delete(injection);
}
injection.active = false;
Expand Down
Expand Up @@ -60,6 +60,13 @@ class Shim {
this.runFirst = opts.runFirst;
this.unblocksOnOptIn = unblocksOnOptIn;
this.requestStorageAccessForRedirect = opts.requestStorageAccessForRedirect;
this.shouldUseScriptingAPI =
browser.aboutConfigPrefs.getBoolPrefSync("useScriptingAPI");
debug(
`WebCompat Shim ${this.id} will be injected using ${
this.shouldUseScriptingAPI ? "scripting" : "contentScripts"
} API`
);

this._hostOptIns = new Set();

Expand All @@ -76,14 +83,26 @@ class Shim {

this.redirectsRequests = !!this.file && matches?.length;

// NOTE: _contentScriptRegistrations is an array of string ids when
// shouldUseScriptingAPI is true and an array of script handles returned
// by contentScripts.register otherwise.
this._contentScriptRegistrations = [];

this.contentScripts = contentScripts || [];
for (const script of this.contentScripts) {
if (typeof script.css === "string") {
script.css = [`/shims/${script.css}`];
script.css = [
this.shouldUseScriptingAPI
? `/shims/${script.css}`
: { file: `/shims/${script.css}` },
];
}
if (typeof script.js === "string") {
script.js = [`/shims/${script.js}`];
script.js = [
this.shouldUseScriptingAPI
? `/shims/${script.js}`
: { file: `/shims/${script.js}` },
];
}
}

Expand Down Expand Up @@ -247,43 +266,48 @@ class Shim {
let idx = 0;
for (const options of this.contentScripts) {
matches.push(options.matches);
// Some shims includes more than one script (e.g. Blogger one contains
// a content script to be run on document_start and one to be run
// on document_end.
options.id = `shim-${this.id}-${idx++}`;
options.persistAcrossSessions = false;
// Having to call getRegisteredContentScripts each time we are going to
// register a Shim content script is suboptimal, but avoiding that
// may require a bit more changes (e.g. rework both Injections, Shim and Shims
// classes to more easily register all content scripts with a single
// call to the scripting API methods when the background script page is loading
// and one per injection or shim being enabled from the AboutCompatBroker).
// In the short term we call getRegisteredContentScripts and restrict it to
// the script id we are about to register.
let isAlreadyRegistered = false;
try {
const registeredScripts =
await browser.scripting.getRegisteredContentScripts({
ids: [options.id],
});
isAlreadyRegistered = !!registeredScripts.length;
} catch (ex) {
console.error(
"Retrieve WebCompat GoFaster registered content scripts failed: ",
ex
);
}
try {
if (!isAlreadyRegistered) {
await browser.scripting.registerContentScripts([options]);
if (this.shouldUseScriptingAPI) {
// Some shims includes more than one script (e.g. Blogger one contains
// a content script to be run on document_start and one to be run
// on document_end.
options.id = `shim-${this.id}-${idx++}`;
options.persistAcrossSessions = false;
// Having to call getRegisteredContentScripts each time we are going to
// register a Shim content script is suboptimal, but avoiding that
// may require a bit more changes (e.g. rework both Injections, Shim and Shims
// classes to more easily register all content scripts with a single
// call to the scripting API methods when the background script page is loading
// and one per injection or shim being enabled from the AboutCompatBroker).
// In the short term we call getRegisteredContentScripts and restrict it to
// the script id we are about to register.
let isAlreadyRegistered = false;
try {
const registeredScripts =
await browser.scripting.getRegisteredContentScripts({
ids: [options.id],
});
isAlreadyRegistered = !!registeredScripts.length;
} catch (ex) {
console.error(
"Retrieve WebCompat GoFaster registered content scripts failed: ",
ex
);
}
this._contentScriptRegistrations.push(options.id);
} catch (ex) {
console.error(
"Registering WebCompat Shim content scripts failed: ",
options,
ex
);
try {
if (!isAlreadyRegistered) {
await browser.scripting.registerContentScripts([options]);
}
this._contentScriptRegistrations.push(options.id);
} catch (ex) {
console.error(
"Registering WebCompat Shim content scripts failed: ",
options,
ex
);
}
} else {
const reg = await browser.contentScripts.register(options);
this._contentScriptRegistrations.push(reg);
}
}
const urls = Array.from(new Set(matches.flat()));
Expand All @@ -292,8 +316,14 @@ class Shim {
}

async _unregisterContentScripts() {
const ids = this._contentScriptRegistrations;
await browser.scripting.unregisterContentScripts({ ids });
if (this.shouldUseScriptingAPI) {
const ids = this._contentScriptRegistrations;
await browser.scripting.unregisterContentScripts({ ids });
} else {
for (const registration of this._contentScriptRegistrations) {
registration.unregister();
}
}
this._contentScriptRegistrations = [];
}

Expand Down
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Mozilla Android Components - Web Compatibility Interventions",
"description": "Urgent post-release fixes for web compatibility.",
"version": "118.2.0",
"version": "118.3.0",
"browser_specific_settings": {
"gecko": {
"id": "webcompat@mozilla.org",
Expand All @@ -17,6 +17,11 @@
"scopes": ["addon_parent"],
"script": "experiment-apis/aboutConfigPrefs.js",
"paths": [["aboutConfigPrefs"]]
},
"child": {
"scopes": ["addon_child"],
"script": "experiment-apis/aboutConfigPrefsChild.js",
"paths": [["aboutConfigPrefs"]]
}
},
"appConstants": {
Expand Down

0 comments on commit 4fe5f5e

Please sign in to comment.