Compare commits

...

9 Commits

Author SHA1 Message Date
Electron Bot
b6315612dd Bump v14.0.0-nightly.20210413 2021-04-13 07:32:14 -07:00
Shelley Vohr
f78f8d15a9 build: better error handling for release builds (#28621) 2021-04-13 09:37:23 +02:00
Samuel Attard
6df2680cb6 refactor: clean up webFrame implementation to use gin wrappers (#28497)
* refactor: clean up webFrame implementation to use gin wrappers

The previous implementation of webFrame in the renderer process leaked
sub-frame contexts and global objects across the context boundaries thus
making it possible for apps to either maliciously or accidentally
violate the contextIsolation boundary.

This re-implementation binds all methods in native code directly to
content::RenderFrame instances instead of relying on JS to provide a
"window" with every method request.  This is much more consistent with
the rest of the Electron codebase and is substantially safer.

* chore: un-re-order for ease of review

* chore: pass isolate around instead of ErrorThrower

* chore: fix rebase typo

* chore: remove unused variables
2021-04-12 16:35:18 -07:00
Electron Bot
e775467e9c Bump v14.0.0-nightly.20210412 2021-04-12 07:34:01 -07:00
Shelley Vohr
2e9ed50bb0 fix: crash on invalid select-serial-port callback (#28602) 2021-04-12 06:18:39 -07:00
Robo
6bd13cc98f fix: load source maps from custom protocols and asar bundles (#28573)
* fix: load source maps from custom protocols and asar bundles

* chore: fix lint
2021-04-11 21:59:36 -07:00
Samuel Maddock
ef4954fa1f docs: systemPreferences.subscribeWorkspaceNotification return type (#28588) 2021-04-11 14:02:58 -07:00
KSneijders
f755c521eb Defined the name of the preload script (#28597) 2021-04-11 14:01:36 -07:00
Shelley Vohr
95e26e2fd4 refactor: use URL API (#28583) 2021-04-09 14:22:18 -07:00
23 changed files with 750 additions and 704 deletions

View File

@@ -1 +1 @@
14.0.0-nightly.20210409
14.0.0-nightly.20210413

View File

@@ -130,6 +130,8 @@ This is necessary for events such as `NSUserDefaultsDidChangeNotification`.
* `userInfo` Record<String, unknown>
* `object` String
Returns `Number` - The ID of this subscription
Same as `subscribeNotification`, but uses `NSWorkspace.sharedWorkspace.notificationCenter`.
This is necessary for events such as `NSWorkspaceDidActivateApplicationNotification`.

View File

@@ -123,7 +123,7 @@ The `index.html` page looks as follows:
#### Define a preload script
Your preload script acts as a bridge between Node.js and your web page. It allows you to expose specific APIs and behaviors to your web page rather than insecurely exposing the entire Node.js API. In this example we will use the preload script to read version information from the `process` object and update the web page with that info.
Your preload script (in our case, the `preload.js` file) acts as a bridge between Node.js and your web page. It allows you to expose specific APIs and behaviors to your web page rather than insecurely exposing the entire Node.js API. In this example we will use the preload script to read version information from the `process` object and update the web page with that info.
```javascript fiddle='docs/fiddles/quick-start'
window.addEventListener('DOMContentLoaded', () => {

View File

@@ -404,6 +404,8 @@ filenames = {
"shell/browser/native_window_observer.h",
"shell/browser/net/asar/asar_url_loader.cc",
"shell/browser/net/asar/asar_url_loader.h",
"shell/browser/net/asar/asar_url_loader_factory.cc",
"shell/browser/net/asar/asar_url_loader_factory.h",
"shell/browser/net/cert_verifier_client.cc",
"shell/browser/net/cert_verifier_client.h",
"shell/browser/net/electron_url_loader_factory.cc",

View File

@@ -1,7 +1,7 @@
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
const binding = process._linkedBinding('electron_renderer_context_bridge');
const contextIsolationEnabled = getWebPreference(window, 'contextIsolation');
const contextIsolationEnabled = mainFrame.getWebPreference('contextIsolation');
const checkContextIsolationEnabled = () => {
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled');

View File

@@ -1,69 +1,3 @@
import { EventEmitter } from 'events';
const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
const binding = process._linkedBinding('electron_renderer_web_frame');
class WebFrame extends EventEmitter {
constructor (public context: Window) {
super();
// Lots of webview would subscribe to webFrame's events.
this.setMaxListeners(0);
}
findFrameByRoutingId (routingId: number) {
return getWebFrame(binding._findFrameByRoutingId(this.context, routingId));
}
getFrameForSelector (selector: string) {
return getWebFrame(binding._getFrameForSelector(this.context, selector));
}
findFrameByName (name: string) {
return getWebFrame(binding._findFrameByName(this.context, name));
}
get opener () {
return getWebFrame(binding._getOpener(this.context));
}
get parent () {
return getWebFrame(binding._getParent(this.context));
}
get top () {
return getWebFrame(binding._getTop(this.context));
}
get firstChild () {
return getWebFrame(binding._getFirstChild(this.context));
}
get nextSibling () {
return getWebFrame(binding._getNextSibling(this.context));
}
get routingId () {
return binding._getRoutingId(this.context);
}
}
// Populate the methods.
for (const name in binding) {
if (!name.startsWith('_')) { // some methods are manually populated above
// TODO(felixrieseberg): Once we can type web_frame natives, we could
// use a neat `keyof` here
(WebFrame as any).prototype[name] = function (...args: Array<any>) {
return (binding as any)[name](this.context, ...args);
};
}
}
// Helper to return WebFrame or null depending on context.
// TODO(zcbenz): Consider returning same WebFrame for the same frame.
function getWebFrame (context: Window) {
return context ? new WebFrame(context) : null;
}
const _webFrame = new WebFrame(window);
export default _webFrame;
export default mainFrame;

View File

@@ -63,18 +63,18 @@ webFrameInit();
// Process command line arguments.
const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_command_line');
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
const contextIsolation = getWebPreference(window, 'contextIsolation');
const nodeIntegration = getWebPreference(window, 'nodeIntegration');
const webviewTag = getWebPreference(window, 'webviewTag');
const isHiddenPage = getWebPreference(window, 'hiddenPage');
const usesNativeWindowOpen = getWebPreference(window, 'nativeWindowOpen');
const rendererProcessReuseEnabled = getWebPreference(window, 'disableElectronSiteInstanceOverrides');
const preloadScript = getWebPreference(window, 'preload');
const preloadScripts = getWebPreference(window, 'preloadScripts');
const guestInstanceId = getWebPreference(window, 'guestInstanceId') || null;
const openerId = getWebPreference(window, 'openerId') || null;
const contextIsolation = mainFrame.getWebPreference('contextIsolation');
const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
const webviewTag = mainFrame.getWebPreference('webviewTag');
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
const usesNativeWindowOpen = mainFrame.getWebPreference('nativeWindowOpen');
const rendererProcessReuseEnabled = mainFrame.getWebPreference('disableElectronSiteInstanceOverrides');
const preloadScript = mainFrame.getWebPreference('preload');
const preloadScripts = mainFrame.getWebPreference('preloadScripts');
const guestInstanceId = mainFrame.getWebPreference('guestInstanceId') || null;
const openerId = mainFrame.getWebPreference('openerId') || null;
const appPath = hasSwitch('app-path') ? getSwitchValue('app-path') : null;
// The webContents preload script is loaded after the session preload scripts.

View File

@@ -3,13 +3,8 @@ import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
// Helper function to resolve url set in attribute.
const a = document.createElement('a');
const resolveURL = function (url?: string | null) {
if (!url) return '';
a.href = url;
return a.href;
return url ? new URL(url, location.href).href : '';
};
interface MutationHandler {

View File

@@ -113,7 +113,7 @@ function preloadRequire (module: string) {
// Process command line arguments.
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
// Similar to nodes --expose-internals flag, this exposes _linkedBinding so
// that tests can call it to get access to some test only bindings
@@ -121,13 +121,13 @@ if (hasSwitch('unsafely-expose-electron-internals-for-testing')) {
preloadProcess._linkedBinding = process._linkedBinding;
}
const contextIsolation = getWebPreference(window, 'contextIsolation');
const webviewTag = getWebPreference(window, 'webviewTag');
const isHiddenPage = getWebPreference(window, 'hiddenPage');
const rendererProcessReuseEnabled = getWebPreference(window, 'disableElectronSiteInstanceOverrides');
const contextIsolation = mainFrame.getWebPreference('contextIsolation');
const webviewTag = mainFrame.getWebPreference('webviewTag');
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
const rendererProcessReuseEnabled = mainFrame.getWebPreference('disableElectronSiteInstanceOverrides');
const usesNativeWindowOpen = true;
const guestInstanceId = getWebPreference(window, 'guestInstanceId') || null;
const openerId = getWebPreference(window, 'openerId') || null;
const guestInstanceId = mainFrame.getWebPreference('guestInstanceId') || null;
const openerId = mainFrame.getWebPreference('openerId') || null;
switch (window.location.protocol) {
case 'devtools:': {

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "14.0.0-nightly.20210409",
"version": "14.0.0-nightly.20210413",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -235,11 +235,14 @@ async function callAppVeyor (targetBranch, job, options) {
method: 'POST'
};
jobRequestedCount++;
const appVeyorResponse = await makeRequest(requestOpts, true).catch(err => {
console.log('Error calling AppVeyor:', err);
});
const buildUrl = `https://ci.appveyor.com/project/electron-bot/${appVeyorJobs[job]}/build/${appVeyorResponse.version}`;
console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`);
try {
const { version } = await makeRequest(requestOpts, true);
const buildUrl = `https://ci.appveyor.com/project/electron-bot/${appVeyorJobs[job]}/build/${version}`;
console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`);
} catch (err) {
console.log('Could not call AppVeyor: ', err);
}
}
function buildCircleCI (targetBranch, options) {
@@ -281,12 +284,16 @@ async function buildVSTS (targetBranch, options) {
'Content-Type': 'application/json'
}
};
jobRequestedCount++;
const vstsResponse = await makeRequest(requestOpts, true).catch(err => {
console.log('Error calling VSTS to get build definitions:', err);
});
const buildToRun = vstsResponse.value.find(build => build.name === options.job);
callVSTSBuild(buildToRun, targetBranch, environmentVariables, vstsURL, vstsToken);
try {
const vstsResponse = await makeRequest(requestOpts, true);
const buildToRun = vstsResponse.value.find(build => build.name === options.job);
callVSTSBuild(buildToRun, targetBranch, environmentVariables, vstsURL, vstsToken);
} catch (err) {
console.log('Problem calling VSTS to get build definitions: ', err);
}
}
async function callVSTSBuild (build, targetBranch, environmentVariables, vstsURL, vstsToken) {
@@ -310,10 +317,13 @@ async function callVSTSBuild (build, targetBranch, environmentVariables, vstsURL
body: JSON.stringify(buildBody),
method: 'POST'
};
const vstsResponse = await makeRequest(requestOpts, true).catch(err => {
console.log(`Error calling VSTS for job ${build.name}`, err);
});
console.log(`VSTS release build request for ${build.name} successful. Check ${vstsResponse._links.web.href} for status.`);
try {
const { _links } = await makeRequest(requestOpts, true);
console.log(`VSTS release build request for ${build.name} successful. Check ${_links.web.href} for status.`);
} catch (err) {
console.log(`Could not call VSTS for job ${build.name}: `, err);
}
}
function runRelease (targetBranch, options) {

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2021 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/net/asar/asar_url_loader_factory.h"
#include <utility>
#include "shell/browser/net/asar/asar_url_loader.h"
namespace electron {
// static
mojo::PendingRemote<network::mojom::URLLoaderFactory>
AsarURLLoaderFactory::Create() {
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
// The AsarURLLoaderFactory will delete itself when there are no more
// receivers - see the SelfDeletingURLLoaderFactory::OnDisconnect method.
new AsarURLLoaderFactory(pending_remote.InitWithNewPipeAndPassReceiver());
return pending_remote;
}
AsarURLLoaderFactory::AsarURLLoaderFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver)
: network::SelfDeletingURLLoaderFactory(std::move(factory_receiver)) {}
AsarURLLoaderFactory::~AsarURLLoaderFactory() = default;
void AsarURLLoaderFactory::CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> loader,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
new net::HttpResponseHeaders(""));
}
} // namespace electron

View File

@@ -0,0 +1,38 @@
// Copyright (c) 2021 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_NET_ASAR_ASAR_URL_LOADER_FACTORY_H_
#define SHELL_BROWSER_NET_ASAR_ASAR_URL_LOADER_FACTORY_H_
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/network/public/cpp/self_deleting_url_loader_factory.h"
namespace electron {
// Provide support for accessing asar archives in file:// protocol.
class AsarURLLoaderFactory : public network::SelfDeletingURLLoaderFactory {
public:
static mojo::PendingRemote<network::mojom::URLLoaderFactory> Create();
private:
AsarURLLoaderFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver);
~AsarURLLoaderFactory() override;
// network::mojom::URLLoaderFactory:
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> loader,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override;
};
} // namespace electron
#endif // SHELL_BROWSER_NET_ASAR_ASAR_URL_LOADER_FACTORY_H_

View File

@@ -2,55 +2,17 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/protocol_registry.h"
#include <memory>
#include <utility>
#include "content/public/browser/web_contents.h"
#include "services/network/public/cpp/self_deleting_url_loader_factory.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/net/asar/asar_url_loader.h"
#include "shell/browser/protocol_registry.h"
#include "shell/browser/net/asar/asar_url_loader_factory.h"
namespace electron {
namespace {
// Provide support for accessing asar archives in file:// protocol.
class AsarURLLoaderFactory : public network::SelfDeletingURLLoaderFactory {
public:
static mojo::PendingRemote<network::mojom::URLLoaderFactory> Create() {
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
// The AsarURLLoaderFactory will delete itself when there are no more
// receivers - see the SelfDeletingURLLoaderFactory::OnDisconnect method.
new AsarURLLoaderFactory(pending_remote.InitWithNewPipeAndPassReceiver());
return pending_remote;
}
private:
AsarURLLoaderFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver)
: network::SelfDeletingURLLoaderFactory(std::move(factory_receiver)) {}
~AsarURLLoaderFactory() override = default;
// network::mojom::URLLoaderFactory:
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> loader,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override {
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
new net::HttpResponseHeaders(""));
}
};
} // namespace
// static
ProtocolRegistry* ProtocolRegistry::FromBrowserContext(
content::BrowserContext* context) {

View File

@@ -30,6 +30,7 @@ class ProtocolRegistry {
bool allow_file_access);
const HandlersMap& intercept_handlers() const { return intercept_handlers_; }
const HandlersMap& handlers() const { return handlers_; }
bool RegisterProtocol(ProtocolType type,
const std::string& scheme,

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 14,0,0,20210409
PRODUCTVERSION 14,0,0,20210409
FILEVERSION 14,0,0,20210413
PRODUCTVERSION 14,0,0,20210413
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -124,9 +124,13 @@ void SerialChooserController::OnDeviceChosen(const std::string& port_id) {
std::find_if(ports_.begin(), ports_.end(), [&port_id](const auto& ptr) {
return ptr->token.ToString() == port_id;
});
chooser_context_->GrantPortPermission(requesting_origin_, embedding_origin_,
*it->get());
RunCallback(it->Clone());
if (it != ports_.end()) {
chooser_context_->GrantPortPermission(requesting_origin_,
embedding_origin_, *it->get());
RunCallback(it->Clone());
} else {
RunCallback(/*port=*/nullptr);
}
}
}

View File

@@ -41,6 +41,8 @@
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "shell/browser/net/asar/asar_url_loader_factory.h"
#include "shell/browser/protocol_registry.h"
#include "shell/browser/ui/inspectable_web_contents_delegate.h"
#include "shell/browser/ui/inspectable_web_contents_view.h"
#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
@@ -673,12 +675,20 @@ void InspectableWebContents::LoadNetworkResource(DispatchCallback callback,
resource_request.site_for_cookies = net::SiteForCookies::FromUrl(gurl);
resource_request.headers.AddHeadersFromString(headers);
auto* protocol_registry = ProtocolRegistry::FromBrowserContext(
GetDevToolsWebContents()->GetBrowserContext());
NetworkResourceLoader::URLLoaderFactoryHolder url_loader_factory;
if (gurl.SchemeIsFile()) {
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote =
content::CreateFileURLLoaderFactory(
base::FilePath() /* profile_path */,
nullptr /* shared_cors_origin_access_list */);
AsarURLLoaderFactory::Create();
url_loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
std::move(pending_remote)));
} else if (protocol_registry->IsProtocolRegistered(gurl.scheme())) {
auto& protocol_handler = protocol_registry->handlers().at(gurl.scheme());
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote =
ElectronURLLoaderFactory::Create(protocol_handler.first,
protocol_handler.second);
url_loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
std::move(pending_remote)));

View File

@@ -17,27 +17,27 @@ ErrorThrower::ErrorThrower() : isolate_(v8::Isolate::GetCurrent()) {}
ErrorThrower::~ErrorThrower() = default;
void ErrorThrower::ThrowError(base::StringPiece err_msg) {
void ErrorThrower::ThrowError(base::StringPiece err_msg) const {
Throw(v8::Exception::Error, err_msg);
}
void ErrorThrower::ThrowTypeError(base::StringPiece err_msg) {
void ErrorThrower::ThrowTypeError(base::StringPiece err_msg) const {
Throw(v8::Exception::TypeError, err_msg);
}
void ErrorThrower::ThrowRangeError(base::StringPiece err_msg) {
void ErrorThrower::ThrowRangeError(base::StringPiece err_msg) const {
Throw(v8::Exception::RangeError, err_msg);
}
void ErrorThrower::ThrowReferenceError(base::StringPiece err_msg) {
void ErrorThrower::ThrowReferenceError(base::StringPiece err_msg) const {
Throw(v8::Exception::ReferenceError, err_msg);
}
void ErrorThrower::ThrowSyntaxError(base::StringPiece err_msg) {
void ErrorThrower::ThrowSyntaxError(base::StringPiece err_msg) const {
Throw(v8::Exception::SyntaxError, err_msg);
}
void ErrorThrower::Throw(ErrorGenerator gen, base::StringPiece err_msg) {
void ErrorThrower::Throw(ErrorGenerator gen, base::StringPiece err_msg) const {
v8::Local<v8::Value> exception = gen(gin::StringToV8(isolate_, err_msg));
if (!isolate_->IsExecutionTerminating())
isolate_->ThrowException(exception);

View File

@@ -17,18 +17,18 @@ class ErrorThrower {
~ErrorThrower();
void ThrowError(base::StringPiece err_msg);
void ThrowTypeError(base::StringPiece err_msg);
void ThrowRangeError(base::StringPiece err_msg);
void ThrowReferenceError(base::StringPiece err_msg);
void ThrowSyntaxError(base::StringPiece err_msg);
void ThrowError(base::StringPiece err_msg) const;
void ThrowTypeError(base::StringPiece err_msg) const;
void ThrowRangeError(base::StringPiece err_msg) const;
void ThrowReferenceError(base::StringPiece err_msg) const;
void ThrowSyntaxError(base::StringPiece err_msg) const;
v8::Isolate* isolate() const { return isolate_; }
private:
using ErrorGenerator =
v8::Local<v8::Value> (*)(v8::Local<v8::String> err_msg);
void Throw(ErrorGenerator gen, base::StringPiece err_msg);
void Throw(ErrorGenerator gen, base::StringPiece err_msg) const;
v8::Isolate* isolate_;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1586,6 +1586,14 @@ describe('navigator.serial', () => {
expect(port).to.equal('NotFoundError: No port selected by the user.');
});
it('does not crash when select-serial-port is called with an invalid port', async () => {
w.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
callback('i-do-not-exist');
});
const port = await getPorts();
expect(port).to.equal('NotFoundError: No port selected by the user.');
});
it('returns a port when select-serial-port event is defined', async () => {
w.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
callback(portList[0].portId);

View File

@@ -114,17 +114,12 @@ declare namespace NodeJS {
webviewTag: boolean;
}
interface InternalWebFrame extends Electron.WebFrame {
getWebPreference<K extends keyof InternalWebPreferences>(name: K): InternalWebPreferences[K];
}
interface WebFrameBinding {
_findFrameByRoutingId(window: Window, routingId: number): Window;
_getFrameForSelector(window: Window, selector: string): Window;
_findFrameByName(window: Window, name: string): Window;
_getOpener(window: Window): Window;
_getParent(window: Window): Window;
_getTop(window: Window): Window;
_getFirstChild(window: Window): Window;
_getNextSibling(window: Window): Window;
_getRoutingId(window: Window): number;
getWebPreference<K extends keyof InternalWebPreferences>(window: Window, name: K): InternalWebPreferences[K];
mainFrame: InternalWebFrame;
}
type DataPipe = {