From 9a7bda51a34b5481ace7a8b9785e54820d1e9815 Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Tue, 5 Jan 2021 03:27:48 +0100 Subject: [PATCH] fix(extensions): implement missing web_request hooks (#22655) (#27096) Co-authored-by: Jeremy Apthorp Co-authored-by: samuelmaddock Co-authored-by: Jeremy Apthorp Co-authored-by: samuelmaddock --- docs/api/extensions.md | 6 + script/gen-filenames.ts | 2 +- shell/browser/electron_browser_client.cc | 47 +++- .../extensions/electron_extension_loader.cc | 20 ++ .../extensions/electron_extensions_client.cc | 4 +- spec-main/extensions-spec.ts | 123 +++++++++-- .../chrome-webRequest-wss/background.js | 12 ++ .../chrome-webRequest-wss/manifest.json | 10 + .../chrome-webRequest/background.js | 9 + .../chrome-webRequest/manifest.json | 10 + spec/ts-smoke/electron/renderer.ts | 204 +++++++++--------- 11 files changed, 319 insertions(+), 128 deletions(-) create mode 100644 spec-main/fixtures/extensions/chrome-webRequest-wss/background.js create mode 100644 spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json create mode 100644 spec-main/fixtures/extensions/chrome-webRequest/background.js create mode 100644 spec-main/fixtures/extensions/chrome-webRequest/manifest.json diff --git a/docs/api/extensions.md b/docs/api/extensions.md index f483d6318b056..3faad34aa8706 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -114,3 +114,9 @@ The following methods of `chrome.management` are supported: - `chrome.management.getPermissionWarningsByManifest` - `chrome.management.onEnabled` - `chrome.management.onDisabled` + +### `chrome.webRequest` + +All features of this API are supported. + +> **NOTE:** Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers. diff --git a/script/gen-filenames.ts b/script/gen-filenames.ts index d9c93c84d471d..56672bc67d4f1 100644 --- a/script/gen-filenames.ts +++ b/script/gen-filenames.ts @@ -101,7 +101,7 @@ ${target.dependencies.map(dep => ` "${dep}",`).join('\n')} `); }; -if (process.mainModule === module) { +if ((process as any).mainModule === module) { main().catch((err) => { console.error(err); process.exit(1); diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 706bb848d750a..809a3fd58fc04 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -140,6 +140,7 @@ #include "content/public/browser/file_url_loader.h" #include "content/public/browser/web_ui_url_loader_factory.h" #include "extensions/browser/api/mime_handler_private/mime_handler_private.h" +#include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_message_filter.h" @@ -1466,7 +1467,17 @@ bool ElectronBrowserClient::WillInterceptWebSocket( if (!web_request.get()) return false; - return web_request->HasListener(); + bool has_listener = web_request->HasListener(); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + const auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + browser_context); + + if (web_request_api) + has_listener |= web_request_api->MayHaveProxies(); +#endif + + return has_listener; } void ElectronBrowserClient::CreateWebSocket( @@ -1480,8 +1491,24 @@ void ElectronBrowserClient::CreateWebSocket( v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); auto* browser_context = frame->GetProcess()->GetBrowserContext(); + auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); DCHECK(web_request.get()); + +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (!web_request->HasListener()) { + auto* web_request_api = extensions::BrowserContextKeyedAPIFactory< + extensions::WebRequestAPI>::Get(browser_context); + + if (web_request_api && web_request_api->MayHaveProxies()) { + web_request_api->ProxyWebSocket(frame, std::move(factory), url, + site_for_cookies.RepresentativeUrl(), + user_agent, std::move(handshake_client)); + return; + } + } +#endif + ProxyingWebSocket::StartProxying( web_request.get(), std::move(factory), url, site_for_cookies.RepresentativeUrl(), user_agent, @@ -1509,6 +1536,24 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); DCHECK(web_request.get()); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (!web_request->HasListener()) { + auto* web_request_api = extensions::BrowserContextKeyedAPIFactory< + extensions::WebRequestAPI>::Get(browser_context); + + DCHECK(web_request_api); + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame_host, render_process_id, type, navigation_id, + ukm_source_id, factory_receiver, header_client); + + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + if (use_proxy_for_web_request) + return true; + } +#endif + auto proxied_receiver = std::move(*factory_receiver); mojo::PendingRemote target_factory_remote; *factory_receiver = target_factory_remote.InitWithNewPipeAndPassReceiver(); diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index b9b8294abc303..7f1dcc3d672a8 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -12,12 +12,15 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/task_runner_util.h" #include "base/threading/thread_restrictions.h" +#include "base/time/time.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/pref_names.h" #include "extensions/common/file_util.h" namespace extensions { @@ -110,6 +113,23 @@ void ElectronExtensionLoader::FinishExtensionLoad( if (extension) { extension_registrar_.AddExtension(extension); } + + // Write extension install time to ExtensionPrefs. This is required by + // WebRequestAPI which calls extensions::ExtensionPrefs::GetInstallTime. + // + // Implementation for writing the pref was based on + // PreferenceAPIBase::SetExtensionControlledPref. + { + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); + ExtensionPrefs::ScopedDictionaryUpdate update( + extension_prefs, extension.get()->id(), + extensions::pref_names::kPrefPreferences); + auto preference = update.Create(); + const base::Time install_time = base::Time().Now(); + preference->SetString("install_time", + base::NumberToString(install_time.ToInternalValue())); + } + std::move(cb).Run(extension.get(), result.second); } diff --git a/shell/common/extensions/electron_extensions_client.cc b/shell/common/extensions/electron_extensions_client.cc index 4022da837881c..803a78dda160d 100644 --- a/shell/common/extensions/electron_extensions_client.cc +++ b/shell/common/extensions/electron_extensions_client.cc @@ -130,9 +130,7 @@ const GURL& ElectronExtensionsClient::GetWebstoreUpdateURL() const { } bool ElectronExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { - // TODO(rockot): Maybe we want to do something else here. For now we accept - // any URL as a blacklist URL because we don't really care. - return true; + return false; } } // namespace electron diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 4b50b9f33fa7d..3accb66a8efef 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -1,12 +1,15 @@ import { expect } from 'chai'; -import { app, session, BrowserWindow, ipcMain, WebContents, Extension } from 'electron/main'; +import { app, session, BrowserWindow, ipcMain, WebContents, Extension, Session } from 'electron/main'; import { closeAllWindows, closeWindow } from './window-helpers'; import * as http from 'http'; import { AddressInfo } from 'net'; import * as path from 'path'; import * as fs from 'fs'; +import * as WebSocket from 'ws'; import { emittedOnce, emittedNTimes } from './events-helpers'; +const uuid = require('uuid'); + const fixtures = path.join(__dirname, 'fixtures'); describe('chrome extensions', () => { @@ -15,6 +18,7 @@ describe('chrome extensions', () => { // NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default. let server: http.Server; let url: string; + let port: string; before(async () => { server = http.createServer((req, res) => { if (req.url === '/cors') { @@ -22,8 +26,19 @@ describe('chrome extensions', () => { } res.end(emptyPage); }); + + const wss = new WebSocket.Server({ noServer: true }); + wss.on('connection', function connection (ws) { + ws.on('message', function incoming (message) { + if (message === 'foo') { + ws.send('bar'); + } + }); + }); + await new Promise(resolve => server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + port = String((server.address() as AddressInfo).port); + url = `http://127.0.0.1:${port}`; resolve(); })); }); @@ -87,7 +102,7 @@ describe('chrome extensions', () => { // extension registry is redirected to the main session. so installing an // extension in an in-memory session results in it being installed in the // default session. - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); w.loadURL(url); @@ -99,7 +114,7 @@ describe('chrome extensions', () => { it('serializes a loaded extension', async () => { const extensionPath = path.join(fixtures, 'extensions', 'red-bg'); const manifest = JSON.parse(fs.readFileSync(path.join(extensionPath, 'manifest.json'), 'utf-8')); - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const extension = await customSession.loadExtension(extensionPath); expect(extension.id).to.be.a('string'); expect(extension.name).to.be.a('string'); @@ -110,7 +125,7 @@ describe('chrome extensions', () => { }); it('removes an extension', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); { const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); @@ -128,7 +143,7 @@ describe('chrome extensions', () => { }); it('lists loaded extensions in getAllExtensions', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); expect(customSession.getAllExtensions()).to.deep.equal([e]); customSession.removeExtension(e.id); @@ -136,13 +151,13 @@ describe('chrome extensions', () => { }); it('gets an extension by id', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); expect(customSession.getExtension(e.id)).to.deep.equal(e); }); it('confines an extension to the session it was loaded in', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); const w = new BrowserWindow({ show: false }); // not in the session w.loadURL(url); @@ -152,7 +167,7 @@ describe('chrome extensions', () => { }); it('loading an extension in a temporary session throws an error', async () => { - const customSession = session.fromPartition(require('uuid').v4()); + const customSession = session.fromPartition(uuid.v4()); await expect(customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'))).to.eventually.be.rejectedWith('Extensions cannot be loaded in a temporary session'); }); @@ -166,7 +181,7 @@ describe('chrome extensions', () => { return result; }; beforeEach(async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${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); @@ -186,7 +201,7 @@ describe('chrome extensions', () => { describe('chrome.runtime', () => { let content: any; before(async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-runtime')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); try { @@ -211,7 +226,7 @@ describe('chrome extensions', () => { describe('chrome.storage', () => { it('stores and retrieves a key', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-storage')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); try { @@ -225,9 +240,75 @@ describe('chrome extensions', () => { }); }); + describe('chrome.webRequest', () => { + function fetch (contents: WebContents, url: string) { + return contents.executeJavaScript(`fetch(${JSON.stringify(url)})`); + } + + let customSession: Session; + let w: BrowserWindow; + + beforeEach(() => { + customSession = session.fromPartition(`persist:${uuid.v4()}`); + w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true, contextIsolation: true } }); + }); + + describe('onBeforeRequest', () => { + it('can cancel http requests', async () => { + await w.loadURL(url); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); + }); + + it('does not cancel http requests when no extension loaded', async () => { + await w.loadURL(url); + await expect(fetch(w.webContents, url)).to.not.be.rejectedWith(TypeError); + }); + }); + + it('does not take precedence over Electron webRequest - http', async () => { + return new Promise((resolve) => { + (async () => { + customSession.webRequest.onBeforeRequest((details, callback) => { + resolve(); + callback({ cancel: true }); + }); + await w.loadURL(url); + + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + fetch(w.webContents, url); + })(); + }); + }); + + it('does not take precedence over Electron webRequest - WebSocket', () => { + return new Promise((resolve) => { + (async () => { + customSession.webRequest.onBeforeSendHeaders(() => { + resolve(); + }); + await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + })(); + }); + }); + + describe('WebSocket', () => { + it('can be proxied', async () => { + await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + customSession.webRequest.onSendHeaders((details) => { + if (details.url.startsWith('ws://')) { + expect(details.requestHeaders.foo).be.equal('bar'); + } + }); + }); + }); + }); + describe('chrome.tabs', () => { it('executeScript', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); await w.loadURL(url); @@ -242,12 +323,12 @@ describe('chrome extensions', () => { }); it('connect', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); await w.loadURL(url); - const portName = require('uuid').v4(); + const portName = uuid.v4(); const message = { method: 'connectTab', args: [portName] }; w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); @@ -258,7 +339,7 @@ describe('chrome extensions', () => { }); it('sendMessage receives the response', async function () { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); await w.loadURL(url); @@ -276,7 +357,7 @@ describe('chrome extensions', () => { describe('background pages', () => { it('loads a lazy background page when sending a message', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); try { @@ -292,7 +373,7 @@ describe('chrome extensions', () => { }); it('can use extension.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w.loadURL(`chrome-extension://${id}/page-get-background.html`); @@ -301,7 +382,7 @@ describe('chrome extensions', () => { }); it('can use extension.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w.loadURL(`chrome-extension://${id}/page-get-background.html`); @@ -310,7 +391,7 @@ describe('chrome extensions', () => { }); it('can use runtime.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w.loadURL(`chrome-extension://${id}/page-runtime-get-background.html`); @@ -370,7 +451,7 @@ describe('chrome extensions', () => { }; it('loads a devtools extension', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); customSession.loadExtension(path.join(fixtures, 'extensions', 'devtools-extension')); const winningMessage = emittedOnce(ipcMain, 'winning'); const w = new BrowserWindow({ show: true, webPreferences: { session: customSession, nodeIntegration: true } }); diff --git a/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js new file mode 100644 index 0000000000000..94ddee976b1ee --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js @@ -0,0 +1,12 @@ +/* global chrome */ + +chrome.webRequest.onBeforeSendHeaders.addListener( + (details) => { + if (details.requestHeaders) { + details.requestHeaders.foo = 'bar'; + } + return { cancel: false, requestHeaders: details.requestHeaders }; + }, + { urls: ['*://127.0.0.1:*'] }, + ['blocking'] +); diff --git a/spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json b/spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json new file mode 100644 index 0000000000000..c1723d2118850 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "chrome-webRequest", + "version": "1.0", + "background": { + "scripts": ["background.js"], + "persistent": true + }, + "permissions": ["webRequest", "webRequestBlocking", ""], + "manifest_version": 2 +} diff --git a/spec-main/fixtures/extensions/chrome-webRequest/background.js b/spec-main/fixtures/extensions/chrome-webRequest/background.js new file mode 100644 index 0000000000000..ba6f9f3f610ea --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest/background.js @@ -0,0 +1,9 @@ +/* global chrome */ + +chrome.webRequest.onBeforeRequest.addListener( + (details) => { + return { cancel: true }; + }, + { urls: ['*://127.0.0.1:*'] }, + ['blocking'] +); diff --git a/spec-main/fixtures/extensions/chrome-webRequest/manifest.json b/spec-main/fixtures/extensions/chrome-webRequest/manifest.json new file mode 100644 index 0000000000000..c1723d2118850 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "chrome-webRequest", + "version": "1.0", + "background": { + "scripts": ["background.js"], + "persistent": true + }, + "permissions": ["webRequest", "webRequestBlocking", ""], + "manifest_version": 2 +} diff --git a/spec/ts-smoke/electron/renderer.ts b/spec/ts-smoke/electron/renderer.ts index bf5ef147e9139..825fe38984aca 100644 --- a/spec/ts-smoke/electron/renderer.ts +++ b/spec/ts-smoke/electron/renderer.ts @@ -7,92 +7,92 @@ import { crashReporter, screen, shell -} from 'electron' +} from 'electron'; -import * as fs from 'fs' +import * as fs from 'fs'; // In renderer process (web page). // https://github.com/electron/electron/blob/master/docs/api/ipc-renderer.md -console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong" +console.log(ipcRenderer.sendSync('synchronous-message', 'ping')); // prints "pong" ipcRenderer.on('asynchronous-reply', (event, arg: any) => { - console.log(arg) // prints "pong" - event.sender.send('another-message', 'Hello World!') -}) + console.log(arg); // prints "pong" + event.sender.send('another-message', 'Hello World!'); +}); -ipcRenderer.send('asynchronous-message', 'ping') +ipcRenderer.send('asynchronous-message', 'ping'); // remote // https://github.com/electron/electron/blob/master/docs/api/remote.md -const BrowserWindow = remote.BrowserWindow -const win = new BrowserWindow({ width: 800, height: 600 }) -win.loadURL('https://github.com') +const BrowserWindow = remote.BrowserWindow; +const win = new BrowserWindow({ width: 800, height: 600 }); +win.loadURL('https://github.com'); remote.getCurrentWindow().on('close', () => { // blabla... -}) +}); remote.getCurrentWindow().capturePage().then(buf => { - fs.writeFile('/tmp/screenshot.png', buf, err => { - console.log(err) - }) -}) + fs.writeFile('/tmp/screenshot.png', buf.toPNG(), err => { + console.log(err); + }); +}); -remote.getCurrentWebContents().print() +remote.getCurrentWebContents().print(); remote.getCurrentWindow().capturePage().then(buf => { remote.require('fs').writeFile('/tmp/screenshot.png', buf, (err: Error) => { - console.log(err) - }) -}) + console.log(err); + }); +}); // web-frame // https://github.com/electron/electron/blob/master/docs/api/web-frame.md -webFrame.setZoomFactor(2) -console.log(webFrame.getZoomFactor()) +webFrame.setZoomFactor(2); +console.log(webFrame.getZoomFactor()); -webFrame.setZoomLevel(200) -console.log(webFrame.getZoomLevel()) +webFrame.setZoomLevel(200); +console.log(webFrame.getZoomLevel()); -webFrame.setVisualZoomLevelLimits(50, 200) +webFrame.setVisualZoomLevelLimits(50, 200); webFrame.setSpellCheckProvider('en-US', { spellCheck (words, callback) { setTimeout(() => { - const spellchecker = require('spellchecker') - const misspelled = words.filter(x => spellchecker.isMisspelled(x)) - callback(misspelled) - }, 0) + const spellchecker = require('spellchecker'); + const misspelled = words.filter(x => spellchecker.isMisspelled(x)); + callback(misspelled); + }, 0); } -}) +}); -webFrame.insertText('text') +webFrame.insertText('text'); -webFrame.executeJavaScript('return true;').then((v: boolean) => console.log(v)) -webFrame.executeJavaScript('return true;', true).then((v: boolean) => console.log(v)) -webFrame.executeJavaScript('return true;', true) -webFrame.executeJavaScript('return true;', true).then((result: boolean) => console.log(result)) +webFrame.executeJavaScript('return true;').then((v: boolean) => console.log(v)); +webFrame.executeJavaScript('return true;', true).then((v: boolean) => console.log(v)); +webFrame.executeJavaScript('return true;', true); +webFrame.executeJavaScript('return true;', true).then((result: boolean) => console.log(result)); -console.log(webFrame.getResourceUsage()) -webFrame.clearCache() +console.log(webFrame.getResourceUsage()); +webFrame.clearCache(); // clipboard // https://github.com/electron/electron/blob/master/docs/api/clipboard.md -clipboard.writeText('Example String') -clipboard.writeText('Example String', 'selection') -console.log(clipboard.readText('selection')) -console.log(clipboard.availableFormats()) -clipboard.clear() +clipboard.writeText('Example String'); +clipboard.writeText('Example String', 'selection'); +console.log(clipboard.readText('selection')); +console.log(clipboard.availableFormats()); +clipboard.clear(); clipboard.write({ html: '', text: 'Hello World!', bookmark: 'Bookmark name', image: clipboard.readImage() -}) +}); // crash-reporter // https://github.com/electron/electron/blob/master/docs/api/crash-reporter.md @@ -102,12 +102,12 @@ crashReporter.start({ companyName: 'YourCompany', submitURL: 'https://your-domain.com/url-to-submit', uploadToServer: true -}) +}); // desktopCapturer // https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md -const desktopCapturer = require('electron').desktopCapturer +const desktopCapturer = require('electron').desktopCapturer; desktopCapturer.getSources({ types: ['window', 'screen'] }).then(sources => { for (let i = 0; i < sources.length; ++i) { @@ -124,18 +124,18 @@ desktopCapturer.getSources({ types: ['window', 'screen'] }).then(sources => { maxHeight: 720 } } - }, gotStream, getUserMediaError) - return + }, gotStream, getUserMediaError); + return; } } -}) +}); function gotStream (stream: any) { - (document.querySelector('video') as HTMLVideoElement).src = URL.createObjectURL(stream) + (document.querySelector('video') as HTMLVideoElement).src = URL.createObjectURL(stream); } function getUserMediaError (error: Error) { - console.log('getUserMediaError', error) + console.log('getUserMediaError', error); } // File object @@ -147,62 +147,62 @@ function getUserMediaError (error: Error) { */ -const holder = document.getElementById('holder') +const holder = document.getElementById('holder'); holder.ondragover = function () { - return false -} + return false; +}; holder.ondragleave = holder.ondragend = function () { - return false -} + return false; +}; holder.ondrop = function (e) { - e.preventDefault() - const file = e.dataTransfer.files[0] - console.log('File you dragged here is', file.path) - return false -} + e.preventDefault(); + const file = e.dataTransfer.files[0]; + console.log('File you dragged here is', file.path); + return false; +}; // nativeImage // https://github.com/electron/electron/blob/master/docs/api/native-image.md -const Tray = remote.Tray -const appIcon2 = new Tray('/Users/somebody/images/icon.png') -const window2 = new BrowserWindow({ icon: '/Users/somebody/images/window.png' }) -const image = clipboard.readImage() -const appIcon3 = new Tray(image) -const appIcon4 = new Tray('/Users/somebody/images/icon.png') +const Tray = remote.Tray; +const appIcon2 = new Tray('/Users/somebody/images/icon.png'); +const window2 = new BrowserWindow({ icon: '/Users/somebody/images/window.png' }); +const image = clipboard.readImage(); +const appIcon3 = new Tray(image); +const appIcon4 = new Tray('/Users/somebody/images/icon.png'); // https://github.com/electron/electron/blob/master/docs/api/process.md // preload.js -const _setImmediate = setImmediate -const _clearImmediate = clearImmediate +const _setImmediate = setImmediate; +const _clearImmediate = clearImmediate; process.once('loaded', function () { - global.setImmediate = _setImmediate - global.clearImmediate = _clearImmediate -}) + global.setImmediate = _setImmediate; + global.clearImmediate = _clearImmediate; +}); // screen // https://github.com/electron/electron/blob/master/docs/api/screen.md -const app = remote.app +const app = remote.app; -let mainWindow: Electron.BrowserWindow = null +let mainWindow: Electron.BrowserWindow = null; app.whenReady().then(() => { - const size = screen.getPrimaryDisplay().workAreaSize - mainWindow = new BrowserWindow({ width: size.width, height: size.height }) -}) + const size = screen.getPrimaryDisplay().workAreaSize; + mainWindow = new BrowserWindow({ width: size.width, height: size.height }); +}); app.whenReady().then(() => { - const displays = screen.getAllDisplays() - let externalDisplay: any = null + const displays = screen.getAllDisplays(); + let externalDisplay: any = null; for (const i in displays) { if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) { - externalDisplay = displays[i] - break + externalDisplay = displays[i]; + break; } } @@ -210,55 +210,55 @@ app.whenReady().then(() => { mainWindow = new BrowserWindow({ x: externalDisplay.bounds.x + 50, y: externalDisplay.bounds.y + 50 - }) + }); } -}) +}); // shell // https://github.com/electron/electron/blob/master/docs/api/shell.md -shell.openExternal('https://github.com').then(() => {}) +shell.openExternal('https://github.com').then(() => {}); // // https://github.com/electron/electron/blob/master/docs/api/web-view-tag.md -const webview = document.createElement('webview') -webview.loadURL('https://github.com') +const webview = document.createElement('webview'); +webview.loadURL('https://github.com'); webview.addEventListener('console-message', function (e) { - console.log('Guest page logged a message:', e.message) -}) + console.log('Guest page logged a message:', e.message); +}); webview.addEventListener('found-in-page', function (e) { if (e.result.finalUpdate) { - webview.stopFindInPage('keepSelection') + webview.stopFindInPage('keepSelection'); } -}) +}); -const requestId = webview.findInPage('test') +const requestId = webview.findInPage('test'); webview.addEventListener('new-window', async e => { - const { shell } = require('electron') - await shell.openExternal(e.url) -}) + const { shell } = require('electron'); + await shell.openExternal(e.url); +}); webview.addEventListener('close', function () { - webview.src = 'about:blank' -}) + webview.src = 'about:blank'; +}); // In embedder page. webview.addEventListener('ipc-message', function (event) { - console.log(event.channel) // Prints "pong" -}) -webview.send('ping') -webview.capturePage().then(image => { console.log(image) }) + console.log(event.channel); // Prints "pong" +}); +webview.send('ping'); +webview.capturePage().then(image => { console.log(image); }); { - const opened: boolean = webview.isDevToolsOpened() - const focused: boolean = webview.isDevToolsFocused() + const opened: boolean = webview.isDevToolsOpened(); + const focused: boolean = webview.isDevToolsFocused(); } // In guest page. ipcRenderer.on('ping', function () { - ipcRenderer.sendToHost('pong') -}) + ipcRenderer.sendToHost('pong'); +});