Browse Source

fix(extensions): define platform info to prevent renderer crash (#25357)

Samuel Maddock 4 years ago
parent
commit
45170fdbd7

+ 1 - 0
BUILD.gn

@@ -642,6 +642,7 @@ source_set("electron_lib") {
       "shell/common/extensions/api",
       "shell/common/extensions/api:extensions_features",
       "//chrome/browser/resources:component_extension_resources",
+      "//components/update_client:update_client",
       "//components/zoom",
       "//extensions/browser",
       "//extensions/browser:core_api_provider",

+ 1 - 0
docs/api/extensions.md

@@ -74,6 +74,7 @@ The following methods of `chrome.runtime` are supported:
 
 - `chrome.runtime.getBackgroundPage`
 - `chrome.runtime.getManifest`
+- `chrome.runtime.getPlatformInfo`
 - `chrome.runtime.getURL`
 - `chrome.runtime.connect`
 - `chrome.runtime.sendMessage`

+ 44 - 4
shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc

@@ -7,6 +7,7 @@
 #include <string>
 
 #include "build/build_config.h"
+#include "components/update_client/update_query_params.h"
 #include "extensions/common/api/runtime.h"
 #include "shell/browser/extensions/electron_extension_system.h"
 
@@ -42,10 +43,49 @@ bool ElectronRuntimeAPIDelegate::CheckForUpdates(
 void ElectronRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {}
 
 bool ElectronRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) {
-  // TODO(nornagon): put useful information here.
-#if defined(OS_LINUX)
-  info->os = api::runtime::PLATFORM_OS_LINUX;
-#endif
+  const char* os = update_client::UpdateQueryParams::GetOS();
+  if (strcmp(os, "mac") == 0) {
+    info->os = extensions::api::runtime::PLATFORM_OS_MAC;
+  } else if (strcmp(os, "win") == 0) {
+    info->os = extensions::api::runtime::PLATFORM_OS_WIN;
+  } else if (strcmp(os, "linux") == 0) {
+    info->os = extensions::api::runtime::PLATFORM_OS_LINUX;
+  } else if (strcmp(os, "openbsd") == 0) {
+    info->os = extensions::api::runtime::PLATFORM_OS_OPENBSD;
+  } else {
+    NOTREACHED();
+    return false;
+  }
+
+  const char* arch = update_client::UpdateQueryParams::GetArch();
+  if (strcmp(arch, "arm") == 0) {
+    info->arch = extensions::api::runtime::PLATFORM_ARCH_ARM;
+  } else if (strcmp(arch, "arm64") == 0) {
+    info->arch = extensions::api::runtime::PLATFORM_ARCH_ARM64;
+  } else if (strcmp(arch, "x86") == 0) {
+    info->arch = extensions::api::runtime::PLATFORM_ARCH_X86_32;
+  } else if (strcmp(arch, "x64") == 0) {
+    info->arch = extensions::api::runtime::PLATFORM_ARCH_X86_64;
+  } else {
+    NOTREACHED();
+    return false;
+  }
+
+  const char* nacl_arch = update_client::UpdateQueryParams::GetNaclArch();
+  if (strcmp(nacl_arch, "arm") == 0) {
+    info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_ARM;
+  } else if (strcmp(nacl_arch, "arm64") == 0) {
+    // Use ARM for ARM64 NaCl, as ARM64 NaCl is not available.
+    info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_ARM;
+  } else if (strcmp(nacl_arch, "x86-32") == 0) {
+    info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_X86_32;
+  } else if (strcmp(nacl_arch, "x86-64") == 0) {
+    info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_X86_64;
+  } else {
+    NOTREACHED();
+    return false;
+  }
+
   return true;
 }  // namespace extensions
 

+ 34 - 33
spec-main/extensions-spec.ts

@@ -40,9 +40,8 @@ describe('chrome extensions', () => {
   it('does not crash when using chrome.management', async () => {
     const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
     const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
-    w.loadURL('about:blank');
+    await w.loadURL('about:blank');
 
-    await emittedOnce(w.webContents, 'dom-ready');
     await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
     const args: any = await emittedOnce(app, 'web-contents-created');
     const wc: Electron.WebContents = args[1];
@@ -60,9 +59,8 @@ describe('chrome extensions', () => {
   it('can open WebSQLDatabase in a background page', async () => {
     const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
     const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
-    w.loadURL('about:blank');
+    await w.loadURL('about:blank');
 
-    await emittedOnce(w.webContents, 'dom-ready');
     await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
     const args: any = await emittedOnce(app, 'web-contents-created');
     const wc: Electron.WebContents = args[1];
@@ -77,8 +75,7 @@ describe('chrome extensions', () => {
     const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
     const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
     const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
-    w.loadURL(`${extension.url}bare-page.html`);
-    await emittedOnce(w.webContents, 'dom-ready');
+    await w.loadURL(`${extension.url}bare-page.html`);
     await expect(fetch(w.webContents, `${url}/cors`)).to.not.be.rejectedWith(TypeError);
   });
 
@@ -90,8 +87,7 @@ describe('chrome extensions', () => {
     const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
     await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
     const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
-    w.loadURL(url);
-    await emittedOnce(w.webContents, 'dom-ready');
+    await w.loadURL(url);
     const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
     expect(bg).to.equal('red');
   });
@@ -145,8 +141,7 @@ describe('chrome extensions', () => {
     const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
     await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
     const w = new BrowserWindow({ show: false }); // not in the session
-    w.loadURL(url);
-    await emittedOnce(w.webContents, 'dom-ready');
+    await w.loadURL(url);
     const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
     expect(bg).to.equal('');
   });
@@ -169,8 +164,7 @@ describe('chrome extensions', () => {
       const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
       extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n'));
       w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } });
-      w.loadURL(url);
-      await emittedOnce(w.webContents, 'dom-ready');
+      await w.loadURL(url);
     });
     it('getAcceptLanguages()', async () => {
       const result = await exec('getAcceptLanguages');
@@ -184,28 +178,37 @@ describe('chrome extensions', () => {
   });
 
   describe('chrome.runtime', () => {
-    let content: any;
-    before(async () => {
+    let w: BrowserWindow;
+    const exec = async (name: string) => {
+      const p = emittedOnce(ipcMain, 'success');
+      await w.webContents.executeJavaScript(`exec('${name}')`);
+      const [, result] = await p;
+      return result;
+    };
+    beforeEach(async () => {
       const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
       await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-runtime'));
-      const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
-      try {
-        w.loadURL(url);
-        await emittedOnce(w.webContents, 'dom-ready');
-        content = JSON.parse(await w.webContents.executeJavaScript('document.documentElement.textContent'));
-        expect(content).to.be.an('object');
-      } finally {
-        w.destroy();
-      }
+      w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } });
+      await w.loadURL(url);
+    });
+    it('getManifest()', async () => {
+      const result = await exec('getManifest');
+      expect(result).to.be.an('object').with.property('name', 'chrome-runtime');
     });
-    it('getManifest()', () => {
-      expect(content.manifest).to.be.an('object').with.property('name', 'chrome-runtime');
+    it('id', async () => {
+      const result = await exec('id');
+      expect(result).to.be.a('string').with.lengthOf(32);
     });
-    it('id', () => {
-      expect(content.id).to.be.a('string').with.lengthOf(32);
+    it('getURL()', async () => {
+      const result = await exec('getURL');
+      expect(result).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/);
     });
-    it('getURL()', () => {
-      expect(content.url).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/);
+    it('getPlatformInfo()', async () => {
+      const result = await exec('getPlatformInfo');
+      expect(result).to.be.an('object');
+      expect(result.os).to.be.a('string');
+      expect(result.arch).to.be.a('string');
+      expect(result.nacl_arch).to.be.a('string');
     });
   });
 
@@ -562,8 +565,7 @@ describe('chrome extensions', () => {
     it('loads a ui page of an extension', async () => {
       const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
       const w = new BrowserWindow({ show: false });
-      w.loadURL(`chrome-extension://${id}/bare-page.html`);
-      await emittedOnce(w.webContents, 'dom-ready');
+      await w.loadURL(`chrome-extension://${id}/bare-page.html`);
       const textContent = await w.webContents.executeJavaScript('document.body.textContent');
       expect(textContent).to.equal('ui page loaded ok\n');
     });
@@ -571,8 +573,7 @@ describe('chrome extensions', () => {
     it('can load resources', async () => {
       const { id } = await session.defaultSession.loadExtension(path.join(fixtures, 'extensions', 'ui-page'));
       const w = new BrowserWindow({ show: false });
-      w.loadURL(`chrome-extension://${id}/page-script-load.html`);
-      await emittedOnce(w.webContents, 'dom-ready');
+      await w.loadURL(`chrome-extension://${id}/page-script-load.html`);
       const textContent = await w.webContents.executeJavaScript('document.body.textContent');
       expect(textContent).to.equal('script loaded ok\n');
     });

+ 12 - 0
spec-main/fixtures/extensions/chrome-runtime/background.js

@@ -0,0 +1,12 @@
+/* global chrome */
+
+chrome.runtime.onMessage.addListener((message, sender, reply) => {
+  switch (message) {
+    case 'getPlatformInfo':
+      chrome.runtime.getPlatformInfo(reply);
+      break;
+  }
+
+  // Respond asynchronously
+  return true;
+});

+ 37 - 4
spec-main/fixtures/extensions/chrome-runtime/main.js

@@ -1,6 +1,39 @@
 /* eslint-disable */
-document.documentElement.textContent = JSON.stringify({
-  manifest: chrome.runtime.getManifest(),
-  id: chrome.runtime.id,
-  url: chrome.runtime.getURL('main.js')
+
+function evalInMainWorld(fn) {
+  const script = document.createElement('script')
+  script.textContent = `((${fn})())`
+  document.documentElement.appendChild(script)
+}
+
+async function exec(name) {
+  let result
+  switch (name) {
+    case 'getManifest':
+      result = chrome.runtime.getManifest()
+      break
+    case 'id':
+      result = chrome.runtime.id
+      break
+    case 'getURL':
+      result = chrome.runtime.getURL('main.js')
+      break
+    case 'getPlatformInfo': {
+      result = await new Promise(resolve => {
+        chrome.runtime.sendMessage(name, resolve)
+      })
+      break
+    }
+  }
+
+  const funcStr = `() => { require('electron').ipcRenderer.send('success', ${JSON.stringify(result)}) }`
+  evalInMainWorld(funcStr)
+}
+
+window.addEventListener('message', event => {
+  exec(event.data.name)
+})
+
+evalInMainWorld(() => {
+  window.exec = name => window.postMessage({ name })
 })

+ 4 - 0
spec-main/fixtures/extensions/chrome-runtime/manifest.json

@@ -8,5 +8,9 @@
       "run_at": "document_end"
     }
   ],
+  "background": {
+    "scripts": ["background.js"],
+    "persistent": false
+  },
   "manifest_version": 2
 }