Skip to content

Commit 45170fd

Browse files
fix(extensions): define platform info to prevent renderer crash (electron#25357)
1 parent fbf32f6 commit 45170fd

File tree

7 files changed

+133
-41
lines changed

7 files changed

+133
-41
lines changed

BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ source_set("electron_lib") {
642642
"shell/common/extensions/api",
643643
"shell/common/extensions/api:extensions_features",
644644
"//chrome/browser/resources:component_extension_resources",
645+
"//components/update_client:update_client",
645646
"//components/zoom",
646647
"//extensions/browser",
647648
"//extensions/browser:core_api_provider",

docs/api/extensions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ The following methods of `chrome.runtime` are supported:
7474

7575
- `chrome.runtime.getBackgroundPage`
7676
- `chrome.runtime.getManifest`
77+
- `chrome.runtime.getPlatformInfo`
7778
- `chrome.runtime.getURL`
7879
- `chrome.runtime.connect`
7980
- `chrome.runtime.sendMessage`

shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <string>
88

99
#include "build/build_config.h"
10+
#include "components/update_client/update_query_params.h"
1011
#include "extensions/common/api/runtime.h"
1112
#include "shell/browser/extensions/electron_extension_system.h"
1213

@@ -42,10 +43,49 @@ bool ElectronRuntimeAPIDelegate::CheckForUpdates(
4243
void ElectronRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {}
4344

4445
bool ElectronRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) {
45-
// TODO(nornagon): put useful information here.
46-
#if defined(OS_LINUX)
47-
info->os = api::runtime::PLATFORM_OS_LINUX;
48-
#endif
46+
const char* os = update_client::UpdateQueryParams::GetOS();
47+
if (strcmp(os, "mac") == 0) {
48+
info->os = extensions::api::runtime::PLATFORM_OS_MAC;
49+
} else if (strcmp(os, "win") == 0) {
50+
info->os = extensions::api::runtime::PLATFORM_OS_WIN;
51+
} else if (strcmp(os, "linux") == 0) {
52+
info->os = extensions::api::runtime::PLATFORM_OS_LINUX;
53+
} else if (strcmp(os, "openbsd") == 0) {
54+
info->os = extensions::api::runtime::PLATFORM_OS_OPENBSD;
55+
} else {
56+
NOTREACHED();
57+
return false;
58+
}
59+
60+
const char* arch = update_client::UpdateQueryParams::GetArch();
61+
if (strcmp(arch, "arm") == 0) {
62+
info->arch = extensions::api::runtime::PLATFORM_ARCH_ARM;
63+
} else if (strcmp(arch, "arm64") == 0) {
64+
info->arch = extensions::api::runtime::PLATFORM_ARCH_ARM64;
65+
} else if (strcmp(arch, "x86") == 0) {
66+
info->arch = extensions::api::runtime::PLATFORM_ARCH_X86_32;
67+
} else if (strcmp(arch, "x64") == 0) {
68+
info->arch = extensions::api::runtime::PLATFORM_ARCH_X86_64;
69+
} else {
70+
NOTREACHED();
71+
return false;
72+
}
73+
74+
const char* nacl_arch = update_client::UpdateQueryParams::GetNaclArch();
75+
if (strcmp(nacl_arch, "arm") == 0) {
76+
info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_ARM;
77+
} else if (strcmp(nacl_arch, "arm64") == 0) {
78+
// Use ARM for ARM64 NaCl, as ARM64 NaCl is not available.
79+
info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_ARM;
80+
} else if (strcmp(nacl_arch, "x86-32") == 0) {
81+
info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_X86_32;
82+
} else if (strcmp(nacl_arch, "x86-64") == 0) {
83+
info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_X86_64;
84+
} else {
85+
NOTREACHED();
86+
return false;
87+
}
88+
4989
return true;
5090
} // namespace extensions
5191

spec-main/extensions-spec.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ describe('chrome extensions', () => {
4040
it('does not crash when using chrome.management', async () => {
4141
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
4242
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
43-
w.loadURL('about:blank');
43+
await w.loadURL('about:blank');
4444

45-
await emittedOnce(w.webContents, 'dom-ready');
4645
await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
4746
const args: any = await emittedOnce(app, 'web-contents-created');
4847
const wc: Electron.WebContents = args[1];
@@ -60,9 +59,8 @@ describe('chrome extensions', () => {
6059
it('can open WebSQLDatabase in a background page', async () => {
6160
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
6261
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
63-
w.loadURL('about:blank');
62+
await w.loadURL('about:blank');
6463

65-
await emittedOnce(w.webContents, 'dom-ready');
6664
await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
6765
const args: any = await emittedOnce(app, 'web-contents-created');
6866
const wc: Electron.WebContents = args[1];
@@ -77,8 +75,7 @@ describe('chrome extensions', () => {
7775
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
7876
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
7977
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
80-
w.loadURL(`${extension.url}bare-page.html`);
81-
await emittedOnce(w.webContents, 'dom-ready');
78+
await w.loadURL(`${extension.url}bare-page.html`);
8279
await expect(fetch(w.webContents, `${url}/cors`)).to.not.be.rejectedWith(TypeError);
8380
});
8481

@@ -90,8 +87,7 @@ describe('chrome extensions', () => {
9087
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
9188
await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
9289
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
93-
w.loadURL(url);
94-
await emittedOnce(w.webContents, 'dom-ready');
90+
await w.loadURL(url);
9591
const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
9692
expect(bg).to.equal('red');
9793
});
@@ -145,8 +141,7 @@ describe('chrome extensions', () => {
145141
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
146142
await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
147143
const w = new BrowserWindow({ show: false }); // not in the session
148-
w.loadURL(url);
149-
await emittedOnce(w.webContents, 'dom-ready');
144+
await w.loadURL(url);
150145
const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
151146
expect(bg).to.equal('');
152147
});
@@ -169,8 +164,7 @@ describe('chrome extensions', () => {
169164
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
170165
extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n'));
171166
w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } });
172-
w.loadURL(url);
173-
await emittedOnce(w.webContents, 'dom-ready');
167+
await w.loadURL(url);
174168
});
175169
it('getAcceptLanguages()', async () => {
176170
const result = await exec('getAcceptLanguages');
@@ -184,28 +178,37 @@ describe('chrome extensions', () => {
184178
});
185179

186180
describe('chrome.runtime', () => {
187-
let content: any;
188-
before(async () => {
181+
let w: BrowserWindow;
182+
const exec = async (name: string) => {
183+
const p = emittedOnce(ipcMain, 'success');
184+
await w.webContents.executeJavaScript(`exec('${name}')`);
185+
const [, result] = await p;
186+
return result;
187+
};
188+
beforeEach(async () => {
189189
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
190190
await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-runtime'));
191-
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
192-
try {
193-
w.loadURL(url);
194-
await emittedOnce(w.webContents, 'dom-ready');
195-
content = JSON.parse(await w.webContents.executeJavaScript('document.documentElement.textContent'));
196-
expect(content).to.be.an('object');
197-
} finally {
198-
w.destroy();
199-
}
191+
w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } });
192+
await w.loadURL(url);
193+
});
194+
it('getManifest()', async () => {
195+
const result = await exec('getManifest');
196+
expect(result).to.be.an('object').with.property('name', 'chrome-runtime');
200197
});
201-
it('getManifest()', () => {
202-
expect(content.manifest).to.be.an('object').with.property('name', 'chrome-runtime');
198+
it('id', async () => {
199+
const result = await exec('id');
200+
expect(result).to.be.a('string').with.lengthOf(32);
203201
});
204-
it('id', () => {
205-
expect(content.id).to.be.a('string').with.lengthOf(32);
202+
it('getURL()', async () => {
203+
const result = await exec('getURL');
204+
expect(result).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/);
206205
});
207-
it('getURL()', () => {
208-
expect(content.url).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/);
206+
it('getPlatformInfo()', async () => {
207+
const result = await exec('getPlatformInfo');
208+
expect(result).to.be.an('object');
209+
expect(result.os).to.be.a('string');
210+
expect(result.arch).to.be.a('string');
211+
expect(result.nacl_arch).to.be.a('string');
209212
});
210213
});
211214

@@ -562,17 +565,15 @@ describe('chrome extensions', () => {
562565
it('loads a ui page of an extension', async () => {
563566
const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
564567
const w = new BrowserWindow({ show: false });
565-
w.loadURL(`chrome-extension://${id}/bare-page.html`);
566-
await emittedOnce(w.webContents, 'dom-ready');
568+
await w.loadURL(`chrome-extension://${id}/bare-page.html`);
567569
const textContent = await w.webContents.executeJavaScript('document.body.textContent');
568570
expect(textContent).to.equal('ui page loaded ok\n');
569571
});
570572

571573
it('can load resources', async () => {
572574
const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
573575
const w = new BrowserWindow({ show: false });
574-
w.loadURL(`chrome-extension://${id}/page-script-load.html`);
575-
await emittedOnce(w.webContents, 'dom-ready');
576+
await w.loadURL(`chrome-extension://${id}/page-script-load.html`);
576577
const textContent = await w.webContents.executeJavaScript('document.body.textContent');
577578
expect(textContent).to.equal('script loaded ok\n');
578579
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* global chrome */
2+
3+
chrome.runtime.onMessage.addListener((message, sender, reply) => {
4+
switch (message) {
5+
case 'getPlatformInfo':
6+
chrome.runtime.getPlatformInfo(reply);
7+
break;
8+
}
9+
10+
// Respond asynchronously
11+
return true;
12+
});
Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
11
/* eslint-disable */
2-
document.documentElement.textContent = JSON.stringify({
3-
manifest: chrome.runtime.getManifest(),
4-
id: chrome.runtime.id,
5-
url: chrome.runtime.getURL('main.js')
2+
3+
function evalInMainWorld(fn) {
4+
const script = document.createElement('script')
5+
script.textContent = `((${fn})())`
6+
document.documentElement.appendChild(script)
7+
}
8+
9+
async function exec(name) {
10+
let result
11+
switch (name) {
12+
case 'getManifest':
13+
result = chrome.runtime.getManifest()
14+
break
15+
case 'id':
16+
result = chrome.runtime.id
17+
break
18+
case 'getURL':
19+
result = chrome.runtime.getURL('main.js')
20+
break
21+
case 'getPlatformInfo': {
22+
result = await new Promise(resolve => {
23+
chrome.runtime.sendMessage(name, resolve)
24+
})
25+
break
26+
}
27+
}
28+
29+
const funcStr = `() => { require('electron').ipcRenderer.send('success', ${JSON.stringify(result)}) }`
30+
evalInMainWorld(funcStr)
31+
}
32+
33+
window.addEventListener('message', event => {
34+
exec(event.data.name)
35+
})
36+
37+
evalInMainWorld(() => {
38+
window.exec = name => window.postMessage({ name })
639
})

spec-main/fixtures/extensions/chrome-runtime/manifest.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,9 @@
88
"run_at": "document_end"
99
}
1010
],
11+
"background": {
12+
"scripts": ["background.js"],
13+
"persistent": false
14+
},
1115
"manifest_version": 2
1216
}

0 commit comments

Comments
 (0)