diff --git a/lib/renderer/api/context-bridge.ts b/lib/renderer/api/context-bridge.ts index 2dcc5bf92e417..9ed9e35469c85 100644 --- a/lib/renderer/api/context-bridge.ts +++ b/lib/renderer/api/context-bridge.ts @@ -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'); diff --git a/lib/renderer/api/web-frame.ts b/lib/renderer/api/web-frame.ts index 46bf32ca5cb59..5fbc9d13893da 100644 --- a/lib/renderer/api/web-frame.ts +++ b/lib/renderer/api/web-frame.ts @@ -1,84 +1,3 @@ -import { EventEmitter } from 'events'; -import deprecate from '@electron/internal/common/api/deprecate'; +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); - } -} - -const contextIsolation = binding.getWebPreference(window, 'contextIsolation'); -const worldSafeExecuteJavaScript = binding.getWebPreference(window, 'worldSafeExecuteJavaScript'); - -const worldSafeJS = worldSafeExecuteJavaScript || !contextIsolation; - -// 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) { - if (!worldSafeJS && name.startsWith('executeJavaScript')) { - deprecate.log(`Security Warning: webFrame.${name} was called without worldSafeExecuteJavaScript enabled. This is considered unsafe. worldSafeExecuteJavaScript will be enabled by default in Electron 12.`); - } - return (binding as any)[name](this.context, ...args); - }; - // TODO(MarshallOfSound): Remove once the above deprecation is removed - if (name.startsWith('executeJavaScript')) { - (WebFrame as any).prototype[`_${name}`] = function (...args: Array) { - 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; diff --git a/lib/renderer/init.ts b/lib/renderer/init.ts index 3bb3c2d6eff7e..e3cb88069b1b6 100644 --- a/lib/renderer/init.ts +++ b/lib/renderer/init.ts @@ -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 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 { mainFrame } = process._linkedBinding('electron_renderer_web_frame'); + +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. diff --git a/lib/renderer/security-warnings.ts b/lib/renderer/security-warnings.ts index bae73abf66360..510abe598a088 100644 --- a/lib/renderer/security-warnings.ts +++ b/lib/renderer/security-warnings.ts @@ -79,7 +79,7 @@ const isLocalhost = function () { */ const isUnsafeEvalEnabled: () => Promise = function () { // Call _executeJavaScript to bypass the world-safe deprecation warning - return webFrame._executeJavaScript(`(${(() => { + return webFrame.executeJavaScript(`(${(() => { try { eval(window.trustedTypes.emptyScript); // eslint-disable-line no-eval } catch { diff --git a/lib/renderer/web-frame-init.ts b/lib/renderer/web-frame-init.ts index 3119689990dec..37225cfb3428a 100644 --- a/lib/renderer/web-frame-init.ts +++ b/lib/renderer/web-frame-init.ts @@ -13,11 +13,6 @@ export const webFrameInit = () => { ipcRendererUtils.handle(IPC_MESSAGES.RENDERER_WEB_FRAME_METHOD, ( event, method: keyof WebFrameMethod, ...args: any[] ) => { - // TODO(MarshallOfSound): Remove once the world-safe-execute-javascript deprecation warning is removed - if (method.startsWith('executeJavaScript')) { - return (webFrame as any)[`_${method}`](...args); - } - // The TypeScript compiler cannot handle the sheer number of // call signatures here and simply gives up. Incorrect invocations // will be caught by "keyof WebFrameMethod" though. diff --git a/lib/sandboxed_renderer/init.ts b/lib/sandboxed_renderer/init.ts index f82f0cbe00587..63e55ac919e80 100644 --- a/lib/sandboxed_renderer/init.ts +++ b/lib/sandboxed_renderer/init.ts @@ -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:': { diff --git a/shell/common/gin_helper/error_thrower.cc b/shell/common/gin_helper/error_thrower.cc index c3af1237285b9..52f5f1a863ff6 100644 --- a/shell/common/gin_helper/error_thrower.cc +++ b/shell/common/gin_helper/error_thrower.cc @@ -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 exception = gen(gin::StringToV8(isolate_, err_msg)); if (!isolate_->IsExecutionTerminating()) isolate_->ThrowException(exception); diff --git a/shell/common/gin_helper/error_thrower.h b/shell/common/gin_helper/error_thrower.h index 2a54021e7e0ff..bebc9b7ae42e0 100644 --- a/shell/common/gin_helper/error_thrower.h +++ b/shell/common/gin_helper/error_thrower.h @@ -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::Local err_msg); - void Throw(ErrorGenerator gen, base::StringPiece err_msg); + void Throw(ErrorGenerator gen, base::StringPiece err_msg) const; v8::Isolate* isolate_; }; diff --git a/shell/renderer/api/electron_api_web_frame.cc b/shell/renderer/api/electron_api_web_frame.cc index 3f2b2ed098181..7ab97aed4d7b8 100644 --- a/shell/renderer/api/electron_api_web_frame.cc +++ b/shell/renderer/api/electron_api_web_frame.cc @@ -16,6 +16,9 @@ #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_frame_visitor.h" #include "content/public/renderer/render_view.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/wrappable.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "shell/common/api/api.mojom.h" #include "shell/common/gin_converters/blink_converter.h" @@ -23,6 +26,7 @@ #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/error_thrower.h" +#include "shell/common/gin_helper/function_template_extensions.h" #include "shell/common/gin_helper/promise.h" #include "shell/common/node_includes.h" #include "shell/common/options_switches.h" @@ -111,17 +115,13 @@ content::RenderFrame* GetRenderFrame(v8::Local value) { #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) -bool SpellCheckWord(v8::Isolate* isolate, - v8::Local window, +bool SpellCheckWord(content::RenderFrame* render_frame, const std::string& word, std::vector* optional_suggestions) { size_t start; size_t length; ElectronRendererClient* client = ElectronRendererClient::Get(); - auto* render_frame = GetRenderFrame(window); - if (!render_frame) - return true; std::u16string w = base::UTF8ToUTF16(word); int id = render_frame->GetRoutingID(); @@ -349,555 +349,640 @@ class SpellCheckerHolder final : public content::RenderFrameObserver { } // namespace -// static -std::set SpellCheckerHolder::instances_; - -void SetName(v8::Local window, const std::string& name) { - GetRenderFrame(window)->GetWebFrame()->SetName( - blink::WebString::FromUTF8(name)); -} - -void SetZoomLevel(gin_helper::ErrorThrower thrower, - v8::Local window, - double level) { - content::RenderFrame* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before webFrame.setZoomLevel could be " - "executed"); - return; - } - - mojo::Remote browser_remote; - render_frame->GetBrowserInterfaceBroker()->GetInterface( - browser_remote.BindNewPipeAndPassReceiver()); - browser_remote->SetTemporaryZoomLevel(level); -} +class WebFrameRenderer : public gin::Wrappable, + public content::RenderFrameObserver { + public: + static gin::WrapperInfo kWrapperInfo; -double GetZoomLevel(gin_helper::ErrorThrower thrower, - v8::Local window) { - double result = 0.0; - content::RenderFrame* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before webFrame.getZoomLevel could be " - "executed"); - return result; + static gin::Handle Create( + v8::Isolate* isolate, + content::RenderFrame* render_frame) { + return gin::CreateHandle(isolate, new WebFrameRenderer(render_frame)); } - mojo::Remote browser_remote; - render_frame->GetBrowserInterfaceBroker()->GetInterface( - browser_remote.BindNewPipeAndPassReceiver()); - browser_remote->DoGetZoomLevel(&result); - return result; -} + explicit WebFrameRenderer(content::RenderFrame* render_frame) + : content::RenderFrameObserver(render_frame) { + DCHECK(render_frame); + } -void SetZoomFactor(gin_helper::ErrorThrower thrower, - v8::Local window, - double factor) { - if (factor < std::numeric_limits::epsilon()) { - thrower.ThrowError("'zoomFactor' must be a double greater than 0.0"); - return; + // gin::Wrappable: + gin::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + return gin::Wrappable::GetObjectTemplateBuilder(isolate) + .SetMethod("getWebFrameId", &WebFrameRenderer::GetWebFrameId) + .SetMethod("setName", &WebFrameRenderer::SetName) + .SetMethod("setZoomLevel", &WebFrameRenderer::SetZoomLevel) + .SetMethod("getZoomLevel", &WebFrameRenderer::GetZoomLevel) + .SetMethod("setZoomFactor", &WebFrameRenderer::SetZoomFactor) + .SetMethod("getZoomFactor", &WebFrameRenderer::GetZoomFactor) + .SetMethod("getWebPreference", &WebFrameRenderer::GetWebPreference) +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + .SetMethod("isWordMisspelled", &WebFrameRenderer::IsWordMisspelled) + .SetMethod("getWordSuggestions", &WebFrameRenderer::GetWordSuggestions) +#endif + .SetMethod("setVisualZoomLevelLimits", + &WebFrameRenderer::SetVisualZoomLevelLimits) + .SetMethod("allowGuestViewElementDefinition", + &WebFrameRenderer::AllowGuestViewElementDefinition) + .SetMethod("insertText", &WebFrameRenderer::InsertText) + .SetMethod("insertCSS", &WebFrameRenderer::InsertCSS) + .SetMethod("removeInsertedCSS", &WebFrameRenderer::RemoveInsertedCSS) + .SetMethod("executeJavaScript", &WebFrameRenderer::ExecuteJavaScript) + .SetMethod("executeJavaScriptInIsolatedWorld", + &WebFrameRenderer::ExecuteJavaScriptInIsolatedWorld) + .SetMethod("setIsolatedWorldInfo", + &WebFrameRenderer::SetIsolatedWorldInfo) + .SetMethod("getResourceUsage", &WebFrameRenderer::GetResourceUsage) + .SetMethod("clearCache", &WebFrameRenderer::ClearCache) + .SetMethod("setSpellCheckProvider", + &WebFrameRenderer::SetSpellCheckProvider) + // Frame navigators + .SetMethod("findFrameByRoutingId", + &WebFrameRenderer::FindFrameByRoutingId) + .SetMethod("getFrameForSelector", + &WebFrameRenderer::GetFrameForSelector) + .SetMethod("findFrameByName", &WebFrameRenderer::FindFrameByName) + .SetProperty("opener", &WebFrameRenderer::GetOpener) + .SetProperty("parent", &WebFrameRenderer::GetFrameParent) + .SetProperty("top", &WebFrameRenderer::GetTop) + .SetProperty("firstChild", &WebFrameRenderer::GetFirstChild) + .SetProperty("nextSibling", &WebFrameRenderer::GetNextSibling) + .SetProperty("routingId", &WebFrameRenderer::GetRoutingId); } - SetZoomLevel(thrower, window, blink::PageZoomFactorToZoomLevel(factor)); -} + const char* GetTypeName() override { return "WebFrameRenderer"; } -double GetZoomFactor(gin_helper::ErrorThrower thrower, - v8::Local window) { - double zoom_level = GetZoomLevel(thrower, window); - return blink::PageZoomLevelToZoomFactor(zoom_level); -} + void OnDestruct() override {} -v8::Local GetWebPreference(v8::Isolate* isolate, - v8::Local window, - std::string pref_name) { - content::RenderFrame* render_frame = GetRenderFrame(window); - const auto& prefs = render_frame->GetBlinkPreferences(); - - if (pref_name == options::kPreloadScripts) { - return gin::ConvertToV8(isolate, prefs.preloads); - } else if (pref_name == options::kDisableElectronSiteInstanceOverrides) { - return gin::ConvertToV8(isolate, - prefs.disable_electron_site_instance_overrides); - } else if (pref_name == options::kBackgroundColor) { - return gin::ConvertToV8(isolate, prefs.background_color); - } else if (pref_name == options::kOpenerID) { - // NOTE: openerId is internal-only. - return gin::ConvertToV8(isolate, prefs.opener_id); - } else if (pref_name == options::kContextIsolation) { - return gin::ConvertToV8(isolate, prefs.context_isolation); - } else if (pref_name == options::kWorldSafeExecuteJavaScript) { - return gin::ConvertToV8(isolate, prefs.world_safe_execute_javascript); - } else if (pref_name == options::kGuestInstanceID) { - // NOTE: guestInstanceId is internal-only. - return gin::ConvertToV8(isolate, prefs.guest_instance_id); - } else if (pref_name == options::kHiddenPage) { - // NOTE: hiddenPage is internal-only. - return gin::ConvertToV8(isolate, prefs.hidden_page); - } else if (pref_name == options::kOffscreen) { - return gin::ConvertToV8(isolate, prefs.offscreen); - } else if (pref_name == options::kPreloadScript) { - return gin::ConvertToV8(isolate, prefs.preload.value()); - } else if (pref_name == options::kNativeWindowOpen) { - return gin::ConvertToV8(isolate, prefs.native_window_open); - } else if (pref_name == options::kNodeIntegration) { - return gin::ConvertToV8(isolate, prefs.node_integration); - } else if (pref_name == options::kNodeIntegrationInWorker) { - return gin::ConvertToV8(isolate, prefs.node_integration_in_worker); - } else if (pref_name == options::kEnableNodeLeakageInRenderers) { - // NOTE: enableNodeLeakageInRenderers is internal-only. - return gin::ConvertToV8(isolate, prefs.node_leakage_in_renderers); - } else if (pref_name == options::kNodeIntegrationInSubFrames) { - return gin::ConvertToV8(isolate, true); -#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) - } else if (pref_name == options::kSpellcheck) { - return gin::ConvertToV8(isolate, prefs.enable_spellcheck); -#endif - } else if (pref_name == options::kPlugins) { - return gin::ConvertToV8(isolate, prefs.enable_plugins); - } else if (pref_name == options::kEnableWebSQL) { - return gin::ConvertToV8(isolate, prefs.enable_websql); - } else if (pref_name == options::kWebviewTag) { - return gin::ConvertToV8(isolate, prefs.webview_tag); - } - return v8::Null(isolate); -} + private: + bool MaybeGetRenderFrame(const gin_helper::ErrorThrower& thrower, + const std::string& method_name, + content::RenderFrame** render_frame_ptr) { + std::string error_msg; + if (!MaybeGetRenderFrame(&error_msg, method_name, render_frame_ptr)) { + thrower.ThrowError(error_msg); + return false; + } + return true; + } -void SetVisualZoomLevelLimits(gin_helper::ErrorThrower thrower, - v8::Local window, - double min_level, - double max_level) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before webFrame.setVisualZoomLevelLimits " - "could be executed"); - return; + bool MaybeGetRenderFrame(std::string* error_msg, + const std::string& method_name, + content::RenderFrame** render_frame_ptr) { + auto* frame = render_frame(); + if (!frame) { + *error_msg = "Render frame was torn down before webFrame." + method_name + + " could be " + "executed"; + return false; + } + *render_frame_ptr = frame; + return true; } - blink::WebFrame* web_frame = render_frame->GetWebFrame(); - web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level); -} + static int GetWebFrameId(v8::Local content_window) { + // Get the WebLocalFrame before (possibly) executing any user-space JS while + // getting the |params|. We track the status of the RenderFrame via an + // observer in case it is deleted during user code execution. + content::RenderFrame* render_frame = GetRenderFrame(content_window); + RenderFrameStatus render_frame_status(render_frame); -void AllowGuestViewElementDefinition(gin_helper::ErrorThrower thrower, - v8::Local window, - v8::Local context, - v8::Local register_cb) { - v8::HandleScope handle_scope(thrower.isolate()); - v8::Context::Scope context_scope(context->CreationContext()); - blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope; - - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before " - "webFrame.allowGuestViewElementDefinition could be executed"); - return; - } - - render_frame->GetWebFrame()->RequestExecuteV8Function( - context->CreationContext(), register_cb, v8::Null(thrower.isolate()), 0, - nullptr, nullptr); -} + if (!render_frame_status.is_ok()) + return -1; -int GetWebFrameId(v8::Local window, - v8::Local content_window) { - // Get the WebLocalFrame before (possibly) executing any user-space JS while - // getting the |params|. We track the status of the RenderFrame via an - // observer in case it is deleted during user code execution. - content::RenderFrame* render_frame = GetRenderFrame(content_window); - RenderFrameStatus render_frame_status(render_frame); + blink::WebLocalFrame* frame = render_frame->GetWebFrame(); + // Parent must exist. + blink::WebFrame* parent_frame = frame->Parent(); + DCHECK(parent_frame); + DCHECK(parent_frame->IsWebLocalFrame()); - if (!render_frame_status.is_ok()) - return -1; + return render_frame->GetRoutingID(); + } - blink::WebLocalFrame* frame = render_frame->GetWebFrame(); - // Parent must exist. - blink::WebFrame* parent_frame = frame->Parent(); - DCHECK(parent_frame); - DCHECK(parent_frame->IsWebLocalFrame()); + void SetName(gin_helper::ErrorThrower thrower, const std::string& name) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "setName", &render_frame)) + return; - return render_frame->GetRoutingID(); -} + render_frame->GetWebFrame()->SetName(blink::WebString::FromUTF8(name)); + } -void SetSpellCheckProvider(gin_helper::Arguments* args, - v8::Local window, - const std::string& language, - v8::Local provider) { - auto context = args->isolate()->GetCurrentContext(); - if (!provider->Has(context, gin::StringToV8(args->isolate(), "spellCheck")) - .ToChecked()) { - args->ThrowError("\"spellCheck\" has to be defined"); - return; - } - - // Remove the old client. - content::RenderFrame* render_frame = GetRenderFrame(window); - if (!render_frame) { - args->ThrowError( - "Render frame was torn down before webFrame.setSpellCheckProvider " - "could be executed"); - return; - } - - auto* existing = SpellCheckerHolder::FromRenderFrame(render_frame); - if (existing) - existing->UnsetAndDestroy(); - - // Set spellchecker for all live frames in the same process or - // in the sandbox mode for all live sub frames to this WebFrame. - auto spell_check_client = - std::make_unique(language, args->isolate(), provider); - FrameSetSpellChecker spell_checker(spell_check_client.get(), render_frame); - - // Attach the spell checker to RenderFrame. - new SpellCheckerHolder(render_frame, std::move(spell_check_client)); -} + void SetZoomLevel(gin_helper::ErrorThrower thrower, double level) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "setZoomLevel", &render_frame)) + return; -void InsertText(gin_helper::ErrorThrower thrower, - v8::Local window, - const std::string& text) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before webFrame.insertText could be " - "executed"); - return; - } - - blink::WebFrame* web_frame = render_frame->GetWebFrame(); - if (web_frame->IsWebLocalFrame()) { - web_frame->ToWebLocalFrame() - ->FrameWidget() - ->GetActiveWebInputMethodController() - ->CommitText(blink::WebString::FromUTF8(text), - blink::WebVector(), blink::WebRange(), 0); + mojo::Remote browser_remote; + render_frame->GetBrowserInterfaceBroker()->GetInterface( + browser_remote.BindNewPipeAndPassReceiver()); + browser_remote->SetTemporaryZoomLevel(level); } -} -std::u16string InsertCSS(v8::Local window, - const std::string& css, - gin_helper::Arguments* args) { - blink::WebDocument::CSSOrigin css_origin = - blink::WebDocument::CSSOrigin::kAuthorOrigin; - - gin_helper::Dictionary options; - if (args->GetNext(&options)) - options.Get("cssOrigin", &css_origin); - - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - args->ThrowError( - "Render frame was torn down before webFrame.insertCSS could be " - "executed"); - return std::u16string(); + double GetZoomLevel(gin_helper::ErrorThrower thrower) { + double result = 0.0; + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "getZoomLevel", &render_frame)) + return result; + + mojo::Remote browser_remote; + render_frame->GetBrowserInterfaceBroker()->GetInterface( + browser_remote.BindNewPipeAndPassReceiver()); + browser_remote->DoGetZoomLevel(&result); + return result; } - blink::WebFrame* web_frame = render_frame->GetWebFrame(); - if (web_frame->IsWebLocalFrame()) { - return web_frame->ToWebLocalFrame() - ->GetDocument() - .InsertStyleSheet(blink::WebString::FromUTF8(css), nullptr, css_origin) - .Utf16(); + void SetZoomFactor(gin_helper::ErrorThrower thrower, double factor) { + if (factor < std::numeric_limits::epsilon()) { + thrower.ThrowError("'zoomFactor' must be a double greater than 0.0"); + return; + } + + SetZoomLevel(thrower, blink::PageZoomFactorToZoomLevel(factor)); } - return std::u16string(); -} -void RemoveInsertedCSS(gin_helper::ErrorThrower thrower, - v8::Local window, - const std::u16string& key) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before webFrame.removeInsertedCSS could be " - "executed"); - return; + double GetZoomFactor(gin_helper::ErrorThrower thrower) { + double zoom_level = GetZoomLevel(thrower); + return blink::PageZoomLevelToZoomFactor(zoom_level); } - blink::WebFrame* web_frame = render_frame->GetWebFrame(); - if (web_frame->IsWebLocalFrame()) { - web_frame->ToWebLocalFrame()->GetDocument().RemoveInsertedStyleSheet( - blink::WebString::FromUTF16(key)); + v8::Local GetWebPreference(v8::Isolate* isolate, + gin_helper::ErrorThrower thrower, + std::string pref_name) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "getWebPreference", &render_frame)) + return v8::Undefined(isolate); + + const auto& prefs = render_frame->GetBlinkPreferences(); + + if (pref_name == options::kPreloadScripts) { + return gin::ConvertToV8(isolate, prefs.preloads); + } else if (pref_name == options::kDisableElectronSiteInstanceOverrides) { + return gin::ConvertToV8(isolate, + prefs.disable_electron_site_instance_overrides); + } else if (pref_name == options::kBackgroundColor) { + return gin::ConvertToV8(isolate, prefs.background_color); + } else if (pref_name == options::kOpenerID) { + // NOTE: openerId is internal-only. + return gin::ConvertToV8(isolate, prefs.opener_id); + } else if (pref_name == options::kContextIsolation) { + return gin::ConvertToV8(isolate, prefs.context_isolation); + } else if (pref_name == options::kWorldSafeExecuteJavaScript) { + return gin::ConvertToV8(isolate, prefs.world_safe_execute_javascript); + } else if (pref_name == options::kGuestInstanceID) { + // NOTE: guestInstanceId is internal-only. + return gin::ConvertToV8(isolate, prefs.guest_instance_id); + } else if (pref_name == options::kHiddenPage) { + // NOTE: hiddenPage is internal-only. + return gin::ConvertToV8(isolate, prefs.hidden_page); + } else if (pref_name == options::kOffscreen) { + return gin::ConvertToV8(isolate, prefs.offscreen); + } else if (pref_name == options::kPreloadScript) { + return gin::ConvertToV8(isolate, prefs.preload.value()); + } else if (pref_name == options::kNativeWindowOpen) { + return gin::ConvertToV8(isolate, prefs.native_window_open); + } else if (pref_name == options::kNodeIntegration) { + return gin::ConvertToV8(isolate, prefs.node_integration); + } else if (pref_name == options::kNodeIntegrationInWorker) { + return gin::ConvertToV8(isolate, prefs.node_integration_in_worker); + } else if (pref_name == options::kEnableNodeLeakageInRenderers) { + // NOTE: enableNodeLeakageInRenderers is internal-only. + return gin::ConvertToV8(isolate, prefs.node_leakage_in_renderers); + } else if (pref_name == options::kNodeIntegrationInSubFrames) { + return gin::ConvertToV8(isolate, true); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + } else if (pref_name == options::kSpellcheck) { + return gin::ConvertToV8(isolate, prefs.enable_spellcheck); +#endif + } else if (pref_name == options::kPlugins) { + return gin::ConvertToV8(isolate, prefs.enable_plugins); + } else if (pref_name == options::kEnableWebSQL) { + return gin::ConvertToV8(isolate, prefs.enable_websql); + } else if (pref_name == options::kWebviewTag) { + return gin::ConvertToV8(isolate, prefs.webview_tag); + } + return v8::Null(isolate); } -} -v8::Local ExecuteJavaScript(gin_helper::Arguments* args, - v8::Local window, - const std::u16string& code) { - v8::Isolate* isolate = args->isolate(); - gin_helper::Promise> promise(isolate); - v8::Local handle = promise.GetHandle(); - - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - promise.RejectWithErrorMessage( - "Render frame was torn down before webFrame.executeJavaScript could be " - "executed"); - return handle; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + bool IsWordMisspelled(gin_helper::ErrorThrower thrower, + const std::string& word) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "isWordMisspelled", &render_frame)) + return false; + + return !SpellCheckWord(render_frame, word, nullptr); } - bool has_user_gesture = false; - args->GetNext(&has_user_gesture); + std::vector GetWordSuggestions( + gin_helper::ErrorThrower thrower, + const std::string& word) { + content::RenderFrame* render_frame; + std::vector suggestions; + if (!MaybeGetRenderFrame(thrower, "getWordSuggestions", &render_frame)) + return suggestions; - ScriptExecutionCallback::CompletionCallback completion_callback; - args->GetNext(&completion_callback); + SpellCheckWord(render_frame, word, &suggestions); + return suggestions; + } +#endif - auto& prefs = render_frame->GetBlinkPreferences(); + void SetVisualZoomLevelLimits(gin_helper::ErrorThrower thrower, + double min_level, + double max_level) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "setVisualZoomLevelLimits", + &render_frame)) + return; - render_frame->GetWebFrame()->RequestExecuteScriptAndReturnValue( - blink::WebScriptSource(blink::WebString::FromUTF16(code)), - has_user_gesture, - new ScriptExecutionCallback(std::move(promise), - prefs.world_safe_execute_javascript, - std::move(completion_callback))); + blink::WebFrame* web_frame = render_frame->GetWebFrame(); + web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level); + } - return handle; -} + void AllowGuestViewElementDefinition(gin_helper::ErrorThrower thrower, + v8::Local context, + v8::Local register_cb) { + v8::HandleScope handle_scope(thrower.isolate()); + v8::Context::Scope context_scope(context->CreationContext()); + blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope; + + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "allowGuestViewElementDefinition", + &render_frame)) + return; + + render_frame->GetWebFrame()->RequestExecuteV8Function( + context->CreationContext(), register_cb, v8::Null(thrower.isolate()), 0, + nullptr, nullptr); + } -v8::Local ExecuteJavaScriptInIsolatedWorld( - gin_helper::Arguments* args, - v8::Local window, - int world_id, - const std::vector& scripts) { - v8::Isolate* isolate = args->isolate(); - gin_helper::Promise> promise(isolate); - v8::Local handle = promise.GetHandle(); - - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - promise.RejectWithErrorMessage( - "Render frame was torn down before " - "webFrame.executeJavaScriptInIsolatedWorld could be executed"); - return handle; + void InsertText(gin_helper::ErrorThrower thrower, const std::string& text) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "insertText", &render_frame)) + return; + + blink::WebFrame* web_frame = render_frame->GetWebFrame(); + if (web_frame->IsWebLocalFrame()) { + web_frame->ToWebLocalFrame() + ->FrameWidget() + ->GetActiveWebInputMethodController() + ->CommitText(blink::WebString::FromUTF8(text), + blink::WebVector(), blink::WebRange(), + 0); + } } - bool has_user_gesture = false; - args->GetNext(&has_user_gesture); + std::u16string InsertCSS(gin_helper::ErrorThrower thrower, + const std::string& css, + gin::Arguments* args) { + blink::WebDocument::CSSOrigin css_origin = + blink::WebDocument::CSSOrigin::kAuthorOrigin; + + gin_helper::Dictionary options; + if (args->GetNext(&options)) + options.Get("cssOrigin", &css_origin); + + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "insertCSS", &render_frame)) + return std::u16string(); + + blink::WebFrame* web_frame = render_frame->GetWebFrame(); + if (web_frame->IsWebLocalFrame()) { + return web_frame->ToWebLocalFrame() + ->GetDocument() + .InsertStyleSheet(blink::WebString::FromUTF8(css), nullptr, + css_origin) + .Utf16(); + } + return std::u16string(); + } - blink::WebLocalFrame::ScriptExecutionType scriptExecutionType = - blink::WebLocalFrame::kSynchronous; - args->GetNext(&scriptExecutionType); + void RemoveInsertedCSS(gin_helper::ErrorThrower thrower, + const std::u16string& key) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "removeInsertedCSS", &render_frame)) + return; - ScriptExecutionCallback::CompletionCallback completion_callback; - args->GetNext(&completion_callback); + blink::WebFrame* web_frame = render_frame->GetWebFrame(); + if (web_frame->IsWebLocalFrame()) { + web_frame->ToWebLocalFrame()->GetDocument().RemoveInsertedStyleSheet( + blink::WebString::FromUTF16(key)); + } + } - std::vector sources; + v8::Local ExecuteJavaScript(gin::Arguments* gin_args, + const std::u16string& code) { + gin_helper::Arguments* args = static_cast(gin_args); - for (const auto& script : scripts) { - std::u16string code; - std::u16string url; - int start_line = 1; - script.Get("url", &url); - script.Get("startLine", &start_line); + v8::Isolate* isolate = args->isolate(); + gin_helper::Promise> promise(isolate); + v8::Local handle = promise.GetHandle(); - if (!script.Get("code", &code)) { - const char* error_message = "Invalid 'code'"; - if (!completion_callback.is_null()) { - std::move(completion_callback) - .Run(v8::Undefined(isolate), - v8::Exception::Error( - v8::String::NewFromUtf8(isolate, error_message) - .ToLocalChecked())); - } - promise.RejectWithErrorMessage(error_message); + content::RenderFrame* render_frame; + std::string error_msg; + if (!MaybeGetRenderFrame(&error_msg, "executeJavaScript", &render_frame)) { + promise.RejectWithErrorMessage(error_msg); return handle; } - sources.emplace_back( - blink::WebScriptSource(blink::WebString::FromUTF16(code), - blink::WebURL(GURL(url)), start_line)); + bool has_user_gesture = false; + args->GetNext(&has_user_gesture); + + ScriptExecutionCallback::CompletionCallback completion_callback; + args->GetNext(&completion_callback); + + auto& prefs = render_frame->GetBlinkPreferences(); + + render_frame->GetWebFrame()->RequestExecuteScriptAndReturnValue( + blink::WebScriptSource(blink::WebString::FromUTF16(code)), + has_user_gesture, + new ScriptExecutionCallback(std::move(promise), + prefs.world_safe_execute_javascript, + std::move(completion_callback))); + + return handle; } - auto& prefs = render_frame->GetBlinkPreferences(); + v8::Local ExecuteJavaScriptInIsolatedWorld( + gin::Arguments* gin_args, + int world_id, + const std::vector& scripts) { + gin_helper::Arguments* args = static_cast(gin_args); + + v8::Isolate* isolate = args->isolate(); + gin_helper::Promise> promise(isolate); + v8::Local handle = promise.GetHandle(); + + content::RenderFrame* render_frame; + std::string error_msg; + if (!MaybeGetRenderFrame(&error_msg, "executeJavaScriptInIsolatedWorld", + &render_frame)) { + promise.RejectWithErrorMessage(error_msg); + return handle; + } - render_frame->GetWebFrame()->RequestExecuteScriptInIsolatedWorld( - world_id, &sources.front(), sources.size(), has_user_gesture, - scriptExecutionType, - new ScriptExecutionCallback(std::move(promise), - prefs.world_safe_execute_javascript, - std::move(completion_callback))); + bool has_user_gesture = false; + args->GetNext(&has_user_gesture); + + blink::WebLocalFrame::ScriptExecutionType scriptExecutionType = + blink::WebLocalFrame::kSynchronous; + args->GetNext(&scriptExecutionType); + + ScriptExecutionCallback::CompletionCallback completion_callback; + args->GetNext(&completion_callback); + + std::vector sources; + + for (const auto& script : scripts) { + std::u16string code; + std::u16string url; + int start_line = 1; + script.Get("url", &url); + script.Get("startLine", &start_line); + + if (!script.Get("code", &code)) { + const char* error_message = "Invalid 'code'"; + if (!completion_callback.is_null()) { + std::move(completion_callback) + .Run(v8::Undefined(isolate), + v8::Exception::Error( + v8::String::NewFromUtf8(isolate, error_message) + .ToLocalChecked())); + } + promise.RejectWithErrorMessage(error_message); + return handle; + } - return handle; -} + sources.emplace_back( + blink::WebScriptSource(blink::WebString::FromUTF16(code), + blink::WebURL(GURL(url)), start_line)); + } -void SetIsolatedWorldInfo(v8::Local window, - int world_id, - const gin_helper::Dictionary& options, - gin_helper::Arguments* args) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - args->ThrowError( - "Render frame was torn down before webFrame.setIsolatedWorldInfo could " - "be executed"); - return; - } - - std::string origin_url, security_policy, name; - options.Get("securityOrigin", &origin_url); - options.Get("csp", &security_policy); - options.Get("name", &name); - - if (!security_policy.empty() && origin_url.empty()) { - args->ThrowError( - "If csp is specified, securityOrigin should also be specified"); - return; - } - - blink::WebIsolatedWorldInfo info; - info.security_origin = blink::WebSecurityOrigin::CreateFromString( - blink::WebString::FromUTF8(origin_url)); - info.content_security_policy = blink::WebString::FromUTF8(security_policy); - info.human_readable_name = blink::WebString::FromUTF8(name); - blink::SetIsolatedWorldInfo(world_id, info); -} + auto& prefs = render_frame->GetBlinkPreferences(); -blink::WebCacheResourceTypeStats GetResourceUsage(v8::Isolate* isolate) { - blink::WebCacheResourceTypeStats stats; - blink::WebCache::GetResourceTypeStats(&stats); - return stats; -} + render_frame->GetWebFrame()->RequestExecuteScriptInIsolatedWorld( + world_id, &sources.front(), sources.size(), has_user_gesture, + scriptExecutionType, + new ScriptExecutionCallback(std::move(promise), + prefs.world_safe_execute_javascript, + std::move(completion_callback))); -#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + return handle; + } -bool IsWordMisspelled(v8::Isolate* isolate, - v8::Local window, - const std::string& word) { - return !SpellCheckWord(isolate, window, word, nullptr); -} + void SetIsolatedWorldInfo(gin_helper::ErrorThrower thrower, + int world_id, + const gin_helper::Dictionary& options) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "setIsolatedWorldInfo", &render_frame)) + return; + + std::string origin_url, security_policy, name; + options.Get("securityOrigin", &origin_url); + options.Get("csp", &security_policy); + options.Get("name", &name); + + if (!security_policy.empty() && origin_url.empty()) { + thrower.ThrowError( + "If csp is specified, securityOrigin should also be specified"); + return; + } -std::vector GetWordSuggestions(v8::Isolate* isolate, - v8::Local window, - const std::string& word) { - std::vector suggestions; - SpellCheckWord(isolate, window, word, &suggestions); - return suggestions; -} + blink::WebIsolatedWorldInfo info; + info.security_origin = blink::WebSecurityOrigin::CreateFromString( + blink::WebString::FromUTF8(origin_url)); + info.content_security_policy = blink::WebString::FromUTF8(security_policy); + info.human_readable_name = blink::WebString::FromUTF8(name); + blink::SetIsolatedWorldInfo(world_id, info); + } -#endif + blink::WebCacheResourceTypeStats GetResourceUsage(v8::Isolate* isolate) { + blink::WebCacheResourceTypeStats stats; + blink::WebCache::GetResourceTypeStats(&stats); + return stats; + } -void ClearCache(v8::Isolate* isolate) { - isolate->IdleNotificationDeadline(0.5); - blink::WebCache::Clear(); - base::MemoryPressureListener::NotifyMemoryPressure( - base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); -} + void ClearCache(v8::Isolate* isolate) { + isolate->IdleNotificationDeadline(0.5); + blink::WebCache::Clear(); + base::MemoryPressureListener::NotifyMemoryPressure( + base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); + } -v8::Local FindFrameByRoutingId(v8::Isolate* isolate, - v8::Local window, - int routing_id) { - content::RenderFrame* render_frame = - content::RenderFrame::FromRoutingID(routing_id); - if (render_frame) - return render_frame->GetWebFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + v8::Local FindFrameByRoutingId(v8::Isolate* isolate, + int routing_id) { + content::RenderFrame* render_frame = + content::RenderFrame::FromRoutingID(routing_id); + if (render_frame) + return WebFrameRenderer::Create(isolate, render_frame).ToV8(); + else + return v8::Null(isolate); + } -v8::Local GetOpener(v8::Isolate* isolate, - v8::Local window) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) - return v8::Null(isolate); + v8::Local GetFrameForSelector(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate, + const std::string& selector) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "getFrameForSelector", &render_frame)) + return v8::Null(isolate); + + blink::WebElement element = + render_frame->GetWebFrame()->GetDocument().QuerySelector( + blink::WebString::FromUTF8(selector)); + if (element.IsNull()) // not found + return v8::Null(isolate); + + blink::WebFrame* frame = blink::WebFrame::FromFrameOwnerElement(element); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } - blink::WebFrame* frame = render_frame->GetWebFrame()->Opener(); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + v8::Local FindFrameByName(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate, + const std::string& name) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "getFrameForSelector", &render_frame)) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->FindFrameByName( + blink::WebString::FromUTF8(name)); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } -// Don't name it as GetParent, Windows has API with same name. -v8::Local GetFrameParent(v8::Isolate* isolate, - v8::Local window) { - blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Parent(); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + v8::Local GetOpener(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "opener", &render_frame)) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->Opener(); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } -v8::Local GetTop(v8::Isolate* isolate, v8::Local window) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) - return v8::Null(isolate); + // Don't name it as GetParent, Windows has API with same name. + v8::Local GetFrameParent(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "parent", &render_frame)) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->Parent(); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } - blink::WebFrame* frame = render_frame->GetWebFrame()->Top(); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + v8::Local GetTop(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "top", &render_frame)) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->Top(); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } -v8::Local GetFirstChild(v8::Isolate* isolate, - v8::Local window) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) - return v8::Null(isolate); + v8::Local GetFirstChild(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "firstChild", &render_frame)) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->FirstChild(); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } - blink::WebFrame* frame = render_frame->GetWebFrame()->FirstChild(); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + v8::Local GetNextSibling(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "nextSibling", &render_frame)) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->NextSibling(); + if (frame && frame->IsWebLocalFrame()) + return WebFrameRenderer::Create( + isolate, + content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame())) + .ToV8(); + else + return v8::Null(isolate); + } -v8::Local GetNextSibling(v8::Isolate* isolate, - v8::Local window) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) - return v8::Null(isolate); + int GetRoutingId(gin_helper::ErrorThrower thrower) { + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "routingId", &render_frame)) + return 0; - blink::WebFrame* frame = render_frame->GetWebFrame()->NextSibling(); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + return render_frame->GetRoutingID(); + } -v8::Local GetFrameForSelector(v8::Isolate* isolate, - v8::Local window, - const std::string& selector) { - blink::WebElement element = - GetRenderFrame(window)->GetWebFrame()->GetDocument().QuerySelector( - blink::WebString::FromUTF8(selector)); - if (element.IsNull()) // not found - return v8::Null(isolate); + void SetSpellCheckProvider(gin_helper::ErrorThrower thrower, + v8::Isolate* isolate, + const std::string& language, + v8::Local provider) { + auto context = isolate->GetCurrentContext(); + if (!provider->Has(context, gin::StringToV8(isolate, "spellCheck")) + .ToChecked()) { + thrower.ThrowError("\"spellCheck\" has to be defined"); + return; + } - blink::WebFrame* frame = blink::WebFrame::FromFrameOwnerElement(element); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + // Remove the old client. + content::RenderFrame* render_frame; + if (!MaybeGetRenderFrame(thrower, "setSpellCheckProvider", &render_frame)) + return; -v8::Local FindFrameByName(v8::Isolate* isolate, - v8::Local window, - const std::string& name) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) - return v8::Null(isolate); + auto* existing = SpellCheckerHolder::FromRenderFrame(render_frame); + if (existing) + existing->UnsetAndDestroy(); - blink::WebFrame* frame = render_frame->GetWebFrame()->FindFrameByName( - blink::WebString::FromUTF8(name)); - if (frame && frame->IsWebLocalFrame()) - return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); - else - return v8::Null(isolate); -} + // Set spellchecker for all live frames in the same process or + // in the sandbox mode for all live sub frames to this WebFrame. + auto spell_check_client = + std::make_unique(language, isolate, provider); + FrameSetSpellChecker spell_checker(spell_check_client.get(), render_frame); -int GetRoutingId(gin_helper::ErrorThrower thrower, - v8::Local window) { - auto* render_frame = GetRenderFrame(window); - if (!render_frame) { - thrower.ThrowError( - "Render frame was torn down before webFrame.getRoutingId could be " - "executed"); - return 0; + // Attach the spell checker to RenderFrame. + new SpellCheckerHolder(render_frame, std::move(spell_check_client)); } +}; - return render_frame->GetRoutingID(); -} +gin::WrapperInfo WebFrameRenderer::kWrapperInfo = {gin::kEmbedderNativeGin}; + +// static +std::set SpellCheckerHolder::instances_; } // namespace api @@ -913,39 +998,8 @@ void Initialize(v8::Local exports, v8::Isolate* isolate = context->GetIsolate(); gin_helper::Dictionary dict(isolate, exports); - dict.SetMethod("setName", &SetName); - dict.SetMethod("setZoomLevel", &SetZoomLevel); - dict.SetMethod("getZoomLevel", &GetZoomLevel); - dict.SetMethod("setZoomFactor", &SetZoomFactor); - dict.SetMethod("getZoomFactor", &GetZoomFactor); - dict.SetMethod("setVisualZoomLevelLimits", &SetVisualZoomLevelLimits); - dict.SetMethod("allowGuestViewElementDefinition", - &AllowGuestViewElementDefinition); - dict.SetMethod("getWebFrameId", &GetWebFrameId); - dict.SetMethod("getWebPreference", &GetWebPreference); - dict.SetMethod("setSpellCheckProvider", &SetSpellCheckProvider); - dict.SetMethod("insertText", &InsertText); - dict.SetMethod("insertCSS", &InsertCSS); - dict.SetMethod("removeInsertedCSS", &RemoveInsertedCSS); - dict.SetMethod("executeJavaScript", &ExecuteJavaScript); - dict.SetMethod("executeJavaScriptInIsolatedWorld", - &ExecuteJavaScriptInIsolatedWorld); - dict.SetMethod("setIsolatedWorldInfo", &SetIsolatedWorldInfo); - dict.SetMethod("getResourceUsage", &GetResourceUsage); -#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) - dict.SetMethod("isWordMisspelled", &IsWordMisspelled); - dict.SetMethod("getWordSuggestions", &GetWordSuggestions); -#endif - dict.SetMethod("clearCache", &ClearCache); - dict.SetMethod("_findFrameByRoutingId", &FindFrameByRoutingId); - dict.SetMethod("_getFrameForSelector", &GetFrameForSelector); - dict.SetMethod("_findFrameByName", &FindFrameByName); - dict.SetMethod("_getOpener", &GetOpener); - dict.SetMethod("_getParent", &GetFrameParent); - dict.SetMethod("_getTop", &GetTop); - dict.SetMethod("_getFirstChild", &GetFirstChild); - dict.SetMethod("_getNextSibling", &GetNextSibling); - dict.SetMethod("_getRoutingId", &GetRoutingId); + dict.Set("mainFrame", + WebFrameRenderer::Create(isolate, GetRenderFrame(exports))); } } // namespace diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index debd09ca24894..e9ee5c508e233 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -115,17 +115,12 @@ declare namespace NodeJS { worldSafeExecuteJavaScript: boolean; } + interface InternalWebFrame extends Electron.WebFrame { + getWebPreference(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(window: Window, name: K): InternalWebPreferences[K]; + mainFrame: InternalWebFrame; } type DataPipe = {