diff --git a/docs/api/extensions.md b/docs/api/extensions.md index c9970d066c7e6..32db577b64727 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -40,6 +40,23 @@ We support the following extensions APIs, with some caveats. Other APIs may additionally be supported, but support for any APIs not listed here is provisional and may be removed. +### Supported Manifest Keys + +- `name` +- `version` +- `author` +- `permissions` +- `content_scripts` +- `default_locale` +- `devtools_page` +- `short_name` +- `host_permissions` (Manifest V3) +- `manifest_version` +- `background` (Manifest V2) +- `minimum_chrome_version` + +See [Manifest file format](https://developer.chrome.com/docs/extensions/mv3/manifest/) for more information about the purpose of each possible key. + ### `chrome.devtools.inspectedWindow` All features of this API are supported. diff --git a/shell/common/extensions/api/_manifest_features.json b/shell/common/extensions/api/_manifest_features.json index b860cc40e2391..248fe3df2be47 100644 --- a/shell/common/extensions/api/_manifest_features.json +++ b/shell/common/extensions/api/_manifest_features.json @@ -7,6 +7,10 @@ // well as feature.h, simple_feature.h, and feature_provider.h. { + "author": { + "channel": "stable", + "extension_types": "all" + }, "content_scripts": { "channel": "stable", "extension_types": ["extension"] @@ -15,8 +19,17 @@ "channel": "stable", "extension_types": ["extension"] }, + "host_permissions": { + "channel": "stable", + "extension_types": ["extension"], + "min_manifest_version": 3 + }, "minimum_chrome_version": { "channel": "stable", "extension_types": ["extension"] + }, + "short_name": { + "channel": "stable", + "extension_types": "all" } } diff --git a/spec/extensions-spec.ts b/spec/extensions-spec.ts index 37a000901101f..0f2e114adc9b4 100644 --- a/spec/extensions-spec.ts +++ b/spec/extensions-spec.ts @@ -69,6 +69,67 @@ describe('chrome extensions', () => { `)).to.eventually.have.property('id'); }); + describe('host_permissions', async () => { + let customSession: Session; + let w: BrowserWindow; + + beforeEach(() => { + customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + w = new BrowserWindow({ + show: false, + webPreferences: { + session: customSession, + sandbox: true + } + }); + }); + + afterEach(closeAllWindows); + + it('recognize malformed host permissions', async () => { + await w.loadURL(url); + + const extPath = path.join(fixtures, 'extensions', 'host-permissions', 'malformed'); + customSession.loadExtension(extPath); + + const warning = await new Promise(resolve => { process.on('warning', resolve); }); + + const malformedHost = /Permission 'malformed_host' is unknown or URL pattern is malformed/; + + expect(warning).to.match(malformedHost); + }); + + it('can grant special privileges to urls with host permissions', async () => { + const extPath = path.join(fixtures, 'extensions', 'host-permissions', 'privileged-tab-info'); + await customSession.loadExtension(extPath); + + await w.loadURL(url); + + const message = { method: 'query' }; + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); + + const [,, responseString] = await once(w.webContents, 'console-message'); + const response = JSON.parse(responseString); + + expect(response).to.have.lengthOf(1); + + const tab = response[0]; + expect(tab).to.have.property('url').that.is.a('string'); + expect(tab).to.have.property('title').that.is.a('string'); + expect(tab).to.have.property('active').that.is.a('boolean'); + expect(tab).to.have.property('autoDiscardable').that.is.a('boolean'); + expect(tab).to.have.property('discarded').that.is.a('boolean'); + expect(tab).to.have.property('groupId').that.is.a('number'); + expect(tab).to.have.property('highlighted').that.is.a('boolean'); + expect(tab).to.have.property('id').that.is.a('number'); + expect(tab).to.have.property('incognito').that.is.a('boolean'); + expect(tab).to.have.property('index').that.is.a('number'); + expect(tab).to.have.property('pinned').that.is.a('boolean'); + expect(tab).to.have.property('selected').that.is.a('boolean'); + expect(tab).to.have.property('windowId').that.is.a('number'); + }); + }); + it('supports minimum_chrome_version manifest key', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ @@ -81,7 +142,7 @@ describe('chrome extensions', () => { await w.loadURL('about:blank'); - const extPath = path.join(fixtures, 'extensions', 'chrome-too-low-version'); + const extPath = path.join(fixtures, 'extensions', 'minimum-chrome-version'); const load = customSession.loadExtension(extPath); await expect(load).to.eventually.be.rejectedWith( `Loading extension at ${extPath} failed with: This extension requires Chromium version 999 or greater.` diff --git a/spec/fixtures/extensions/host-permissions/malformed/manifest.json b/spec/fixtures/extensions/host-permissions/malformed/manifest.json new file mode 100644 index 0000000000000..0e4ce8c20271e --- /dev/null +++ b/spec/fixtures/extensions/host-permissions/malformed/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "malformed", + "version": "0.1", + "manifest_version": 3, + "description": "Extension with invalid host_permissions", + "host_permissions": [ + "malformed_host" + ] +} \ No newline at end of file diff --git a/spec/fixtures/extensions/host-permissions/privileged-tab-info/background.js b/spec/fixtures/extensions/host-permissions/privileged-tab-info/background.js new file mode 100644 index 0000000000000..7725abe038b66 --- /dev/null +++ b/spec/fixtures/extensions/host-permissions/privileged-tab-info/background.js @@ -0,0 +1,6 @@ +/* global chrome */ + +chrome.runtime.onMessage.addListener((_request, _sender, sendResponse) => { + chrome.tabs.query({}).then(sendResponse); + return true; +}); diff --git a/spec/fixtures/extensions/host-permissions/privileged-tab-info/main.js b/spec/fixtures/extensions/host-permissions/privileged-tab-info/main.js new file mode 100644 index 0000000000000..2e9cd5b5d9d91 --- /dev/null +++ b/spec/fixtures/extensions/host-permissions/privileged-tab-info/main.js @@ -0,0 +1,11 @@ +/* global chrome */ + +chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { + sendResponse(request); +}); + +window.addEventListener('message', () => { + chrome.runtime.sendMessage({ method: 'query' }, response => { + console.log(JSON.stringify(response)); + }); +}, false); diff --git a/spec/fixtures/extensions/host-permissions/privileged-tab-info/manifest.json b/spec/fixtures/extensions/host-permissions/privileged-tab-info/manifest.json new file mode 100644 index 0000000000000..55dfe2696d740 --- /dev/null +++ b/spec/fixtures/extensions/host-permissions/privileged-tab-info/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "privileged-tab-info", + "version": "0.1", + "manifest_version": 3, + "content_scripts": [{ + "matches": [ ""], + "js": ["main.js"], + "run_at": "document_start" + }], + "host_permissions": ["http://*/*"], + "background": { + "service_worker": "background.js" + } +} \ No newline at end of file diff --git a/spec/fixtures/extensions/chrome-too-low-version/main.js b/spec/fixtures/extensions/minimum-chrome-version/main.js similarity index 100% rename from spec/fixtures/extensions/chrome-too-low-version/main.js rename to spec/fixtures/extensions/minimum-chrome-version/main.js diff --git a/spec/fixtures/extensions/chrome-too-low-version/manifest.json b/spec/fixtures/extensions/minimum-chrome-version/manifest.json similarity index 100% rename from spec/fixtures/extensions/chrome-too-low-version/manifest.json rename to spec/fixtures/extensions/minimum-chrome-version/manifest.json