Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web extension patches #9068

Merged
merged 9 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions packages/packagers/webextension/src/WebExtensionPackager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {Packager} from '@parcel/plugin';
import {replaceURLReferences, relativeBundlePath} from '@parcel/utils';

export default (new Packager({
async package({bundle, bundleGraph, options}) {
async package({bundle, bundleGraph}) {
let assets = [];
bundle.traverseAssets(asset => {
assets.push(asset);
Expand Down Expand Up @@ -43,14 +43,24 @@ export default (new Packager({

contentScript.css = [
...new Set(
(contentScript.css || []).concat(
srcBundles
.flatMap(b => bundleGraph.getReferencedBundles(b))
.filter(b => b.type == 'css')
.map(relPath),
),
srcBundles
.flatMap(b => bundleGraph.getReferencedBundles(b))
.filter(b => b.type == 'css')
.map(relPath)
.concat(contentScript.css || []),
),
];

contentScript.js = [
...new Set(
srcBundles
.flatMap(b => bundleGraph.getReferencedBundles(b))
.filter(b => b.type == 'js')
.map(relPath)
.concat(contentScript.js || []),
),
];

const resources = srcBundles
.flatMap(b => {
const children = [];
Expand Down Expand Up @@ -80,10 +90,6 @@ export default (new Packager({
}
}

if (manifest.manifest_version == 3 && options.hmrOptions) {
war.push({matches: ['<all_urls>'], resources: ['__parcel_hmr_proxy__']});
}

const warResult = (manifest.web_accessible_resources || []).concat(
manifest.manifest_version == 2
? [...new Set(war.flatMap(entry => entry.resources))]
Expand Down
48 changes: 25 additions & 23 deletions packages/runtimes/hmr/src/loaders/hmr-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,26 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') {
!/localhost|127.0.0.1|0.0.0.0/.test(hostname))
? 'wss'
: 'ws';
var ws = new WebSocket(
protocol + '://' + hostname + (port ? ':' + port : '') + '/',
);

var ws;
try {
ws = new WebSocket(
mischnic marked this conversation as resolved.
Show resolved Hide resolved
protocol + '://' + hostname + (port ? ':' + port : '') + '/',
);
} catch (err) {
if (err.message) {
console.error(err.message);
}
ws = {};
}

// Web extension context
var extCtx =
typeof chrome === 'undefined'
? typeof browser === 'undefined'
typeof browser === 'undefined'
? typeof chrome === 'undefined'
? null
: browser
: chrome;
: chrome
: browser;

// Safari doesn't support sourceURL in error stacks.
// eval may also be disabled via CSP, so do a quick check.
Expand Down Expand Up @@ -206,7 +215,9 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') {
}
};
ws.onerror = function (e) {
console.error(e.message);
if (e.message) {
console.error(e.message);
}
};
ws.onclose = function (e) {
if (process.env.PARCEL_BUILD_ENV !== 'test') {
Expand Down Expand Up @@ -400,25 +411,16 @@ async function hmrApplyUpdates(assets) {
if (!supportsSourceURL) {
let promises = assets.map(asset =>
hmrDownload(asset)?.catch(err => {
// Web extension bugfix for Chromium
// https://bugs.chromium.org/p/chromium/issues/detail?id=1255412#c12
// Web extension fix
if (
extCtx &&
extCtx.runtime &&
extCtx.runtime.getManifest().manifest_version == 3
extCtx.runtime.getManifest().manifest_version == 3 &&
typeof ServiceWorkerGlobalScope != 'undefined' &&
global instanceof ServiceWorkerGlobalScope
) {
if (
typeof ServiceWorkerGlobalScope != 'undefined' &&
global instanceof ServiceWorkerGlobalScope
) {
extCtx.runtime.reload();
return;
}
asset.url = extCtx.runtime.getURL(
'/__parcel_hmr_proxy__?url=' +
encodeURIComponent(asset.url + '?t=' + Date.now()),
);
return hmrDownload(asset);
extCtx.runtime.reload();
return;
}
throw err;
}),
Expand Down
46 changes: 31 additions & 15 deletions packages/runtimes/webextension/src/WebExtensionRuntime.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow strict-local

import {Runtime} from '@parcel/plugin';
import {replaceURLReferences} from '@parcel/utils';
import nullthrows from 'nullthrows';
import fs from 'fs';
import path from 'path';
Expand All @@ -11,14 +12,15 @@ const AUTORELOAD_BG = fs.readFileSync(
);

export default (new Runtime({
apply({bundle, bundleGraph, options}) {
loadConfig({config}) {
config.invalidateOnBuild();
},
async apply({bundle, bundleGraph, options}) {
if (!bundle.env.isBrowser() || bundle.env.isWorklet()) {
return;
}
if (bundle.name == 'manifest.json') {
const asset = bundle.getMainEntry();
if (asset?.meta.webextEntry !== true) return;

if (bundle.getMainEntry()?.meta.webextEntry === true) {
// Hack to bust packager cache when any descendants update
const descendants = [];
bundleGraph.traverseBundles(b => {
Expand All @@ -35,7 +37,7 @@ export default (new Runtime({
.find(b => b.getMainEntry()?.meta.webextEntry === true);
const entry = manifest?.getMainEntry();
const insertDep = entry?.meta.webextBGInsert;
if (insertDep == null) return;
if (!manifest || !entry || insertDep == null) return;
const insertBundle = bundleGraph.getReferencedBundle(
nullthrows(entry?.getDependencies().find(dep => dep.id === insertDep)),
nullthrows(manifest),
Expand All @@ -50,16 +52,30 @@ export default (new Runtime({

// Add autoreload
if (bundle === firstInsertableBundle) {
return {
filePath: __filename,
code:
`var HMR_HOST = ${JSON.stringify(
options.hmrOptions?.host ?? 'localhost',
)};` +
`var HMR_PORT = '${options.hmrOptions?.port ?? ''}';` +
AUTORELOAD_BG,
isEntry: true,
};
return [
{
filePath: __filename,
code: AUTORELOAD_BG,
isEntry: true,
},
{
filePath: __filename,
// cache bust on non-asset manifest.json changes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why exactly are all of these caching workaround necessary here (this and invalidateOnBuild above)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be the only way to make the extension reload (i.e. have an HMR update sent out) when the manifest.json changes. I tried many other strategies but none seemed to work.

code: `JSON.parse(${JSON.stringify(
JSON.stringify(
JSON.parse(
replaceURLReferences({
bundle: manifest,
bundleGraph,
contents: await entry.getCode(),
getReplacement: () => '',
}).contents,
),
),
)})`,
isEntry: true,
},
];
}
}
},
Expand Down
64 changes: 40 additions & 24 deletions packages/runtimes/webextension/src/autoreload-bg.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,44 @@
/* global chrome, browser, addEventListener, HMR_HOST, HMR_PORT */
var env = typeof chrome == 'undefined' ? browser : chrome;
env.runtime.onMessage.addListener(function (msg) {
/* global chrome, browser */
let env = typeof browser === 'undefined' ? chrome : browser;
let origReload = env.runtime.reload;
let avoidID = -1;

let promisify =
(obj, fn) =>
(...args) => {
if (typeof browser === 'undefined') {
return new Promise((resolve, reject) =>
obj[fn](...args, res =>
env.runtime.lastError ? reject(env.runtime.lastError) : resolve(res),
),
);
}
return obj[fn](...args);
};

let queryTabs = promisify(env.tabs, 'query');
let messageTab = promisify(env.tabs, 'sendMessage');

env.runtime.reload = () => {
queryTabs({})
.then(tabs => {
return Promise.all(
tabs.map(tab => {
if (tab.id === avoidID) return;
return messageTab(tab.id, {
__parcel_hmr_reload__: true,
}).catch(() => {});
}),
);
})
.then(() => {
origReload.call(env.runtime);
});
};

env.runtime.onMessage.addListener((msg, sender) => {
if (msg.__parcel_hmr_reload__) {
avoidID = sender.tab.id;
env.runtime.reload();
}
});

if (env.runtime.getManifest().manifest_version == 3) {
var proxyLoc = env.runtime.getURL('/__parcel_hmr_proxy__?url=');
addEventListener('fetch', function (evt) {
var url = evt.request.url;
if (url.startsWith(proxyLoc)) {
url = new URL(decodeURIComponent(url.slice(proxyLoc.length)));
if (url.hostname == HMR_HOST && url.port == HMR_PORT) {
evt.respondWith(
fetch(url).then(function (res) {
return new Response(res.body, {
headers: {
'Content-Type': res.headers.get('Content-Type'),
},
});
}),
);
}
}
});
}
Loading
Loading