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
This commit is contained in:
Samuel Attard 2021-04-12 16:35:18 -07:00 committed by GitHub
parent e775467e9c
commit 6df2680cb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 604 additions and 632 deletions

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

@ -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

@ -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_;
};

View file

@ -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<v8::Value> value) {
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
bool SpellCheckWord(v8::Isolate* isolate,
v8::Local<v8::Value> window,
bool SpellCheckWord(content::RenderFrame* render_frame,
const std::string& word,
std::vector<std::u16string>* 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();
@ -206,9 +206,9 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
if (!result.empty()) {
if (!result[0].IsEmpty()) {
v8::Local<v8::Value> value = result[0];
// Either world safe results are disabled or the result was created in
// the same world as the caller or the result is not an object and
// therefore does not have a prototype chain to protect
// Either the result was created in the same world as the caller
// or the result is not an object and therefore does not have a
// prototype chain to protect
bool should_clone_value =
!(value->IsObject() &&
promise_.GetContext() ==
@ -344,70 +344,149 @@ class SpellCheckerHolder final : public content::RenderFrameObserver {
} // namespace
// static
std::set<SpellCheckerHolder*> SpellCheckerHolder::instances_;
class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
public content::RenderFrameObserver {
public:
static gin::WrapperInfo kWrapperInfo;
void SetName(v8::Local<v8::Value> window, const std::string& name) {
GetRenderFrame(window)->GetWebFrame()->SetName(
blink::WebString::FromUTF8(name));
}
void SetZoomLevel(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> 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;
static gin::Handle<WebFrameRenderer> Create(
v8::Isolate* isolate,
content::RenderFrame* render_frame) {
return gin::CreateHandle(isolate, new WebFrameRenderer(render_frame));
}
explicit WebFrameRenderer(content::RenderFrame* render_frame)
: content::RenderFrameObserver(render_frame) {
DCHECK(render_frame);
}
// gin::Wrappable:
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return gin::Wrappable<WebFrameRenderer>::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);
}
const char* GetTypeName() override { return "WebFrameRenderer"; }
void OnDestruct() override {}
private:
bool MaybeGetRenderFrame(v8::Isolate* isolate,
const std::string& method_name,
content::RenderFrame** render_frame_ptr) {
std::string error_msg;
if (!MaybeGetRenderFrame(&error_msg, method_name, render_frame_ptr)) {
gin_helper::ErrorThrower(isolate).ThrowError(error_msg);
return false;
}
return true;
}
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;
}
void SetName(v8::Isolate* isolate, const std::string& name) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "setName", &render_frame))
return;
render_frame->GetWebFrame()->SetName(blink::WebString::FromUTF8(name));
}
void SetZoomLevel(v8::Isolate* isolate, double level) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "setZoomLevel", &render_frame))
return;
mojo::Remote<mojom::ElectronBrowser> browser_remote;
render_frame->GetBrowserInterfaceBroker()->GetInterface(
browser_remote.BindNewPipeAndPassReceiver());
browser_remote->SetTemporaryZoomLevel(level);
}
double GetZoomLevel(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> 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;
}
double GetZoomLevel(v8::Isolate* isolate) {
double result = 0.0;
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "getZoomLevel", &render_frame))
return result;
mojo::Remote<mojom::ElectronBrowser> browser_remote;
render_frame->GetBrowserInterfaceBroker()->GetInterface(
browser_remote.BindNewPipeAndPassReceiver());
browser_remote->DoGetZoomLevel(&result);
return result;
}
}
void SetZoomFactor(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> window,
double factor) {
void SetZoomFactor(gin_helper::ErrorThrower thrower, double factor) {
if (factor < std::numeric_limits<double>::epsilon()) {
thrower.ThrowError("'zoomFactor' must be a double greater than 0.0");
return;
}
SetZoomLevel(thrower, window, blink::PageZoomFactorToZoomLevel(factor));
}
SetZoomLevel(thrower.isolate(), blink::PageZoomFactorToZoomLevel(factor));
}
double GetZoomFactor(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> window) {
double zoom_level = GetZoomLevel(thrower, window);
double GetZoomFactor(v8::Isolate* isolate) {
double zoom_level = GetZoomLevel(isolate);
return blink::PageZoomLevelToZoomFactor(zoom_level);
}
}
v8::Local<v8::Value> GetWebPreference(v8::Isolate* isolate,
v8::Local<v8::Value> window,
v8::Local<v8::Value> GetWebPreference(v8::Isolate* isolate,
std::string pref_name) {
content::RenderFrame* render_frame = GetRenderFrame(window);
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "getWebPreference", &render_frame))
return v8::Undefined(isolate);
const auto& prefs = render_frame->GetBlinkPreferences();
if (pref_name == options::kPreloadScripts) {
@ -455,47 +534,38 @@ v8::Local<v8::Value> GetWebPreference(v8::Isolate* isolate,
return gin::ConvertToV8(isolate, prefs.webview_tag);
}
return v8::Null(isolate);
}
}
void SetVisualZoomLevelLimits(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> window,
void SetVisualZoomLevelLimits(v8::Isolate* isolate,
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");
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "setVisualZoomLevelLimits",
&render_frame))
return;
}
blink::WebFrame* web_frame = render_frame->GetWebFrame();
web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level);
}
}
void AllowGuestViewElementDefinition(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> window,
void AllowGuestViewElementDefinition(v8::Isolate* isolate,
v8::Local<v8::Object> context,
v8::Local<v8::Function> register_cb) {
v8::HandleScope handle_scope(thrower.isolate());
v8::HandleScope handle_scope(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");
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "allowGuestViewElementDefinition",
&render_frame))
return;
}
render_frame->GetWebFrame()->RequestExecuteV8Function(
context->CreationContext(), register_cb, v8::Null(thrower.isolate()), 0,
nullptr, nullptr);
}
context->CreationContext(), register_cb, v8::Null(isolate), 0, nullptr,
nullptr);
}
int GetWebFrameId(v8::Local<v8::Value> window,
v8::Local<v8::Value> content_window) {
static int GetWebFrameId(v8::Local<v8::Value> 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.
@ -512,27 +582,23 @@ int GetWebFrameId(v8::Local<v8::Value> window,
DCHECK(parent_frame->IsWebLocalFrame());
return render_frame->GetRoutingID();
}
}
void SetSpellCheckProvider(gin_helper::Arguments* args,
v8::Local<v8::Value> window,
void SetSpellCheckProvider(gin_helper::ErrorThrower thrower,
v8::Isolate* isolate,
const std::string& language,
v8::Local<v8::Object> provider) {
auto context = args->isolate()->GetCurrentContext();
if (!provider->Has(context, gin::StringToV8(args->isolate(), "spellCheck"))
auto context = isolate->GetCurrentContext();
if (!provider->Has(context, gin::StringToV8(isolate, "spellCheck"))
.ToChecked()) {
args->ThrowError("\"spellCheck\" has to be defined");
thrower.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");
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "setSpellCheckProvider", &render_frame))
return;
}
auto* existing = SpellCheckerHolder::FromRenderFrame(render_frame);
if (existing)
@ -541,37 +607,32 @@ void SetSpellCheckProvider(gin_helper::Arguments* args,
// 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<SpellCheckClient>(language, args->isolate(), provider);
std::make_unique<SpellCheckClient>(language, 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 InsertText(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> 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;
}
void InsertText(v8::Isolate* isolate, const std::string& text) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "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<ui::ImeTextSpan>(), blink::WebRange(), 0);
blink::WebVector<ui::ImeTextSpan>(), blink::WebRange(),
0);
}
}
}
std::u16string InsertCSS(v8::Local<v8::Value> window,
std::u16string InsertCSS(v8::Isolate* isolate,
const std::string& css,
gin_helper::Arguments* args) {
gin::Arguments* args) {
blink::WebDocument::CSSOrigin css_origin =
blink::WebDocument::CSSOrigin::kAuthorOrigin;
@ -579,54 +640,45 @@ std::u16string InsertCSS(v8::Local<v8::Value> window,
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");
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "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)
.InsertStyleSheet(blink::WebString::FromUTF8(css), nullptr,
css_origin)
.Utf16();
}
return std::u16string();
}
void RemoveInsertedCSS(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> 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;
}
void RemoveInsertedCSS(v8::Isolate* isolate, const std::u16string& key) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "removeInsertedCSS", &render_frame))
return;
blink::WebFrame* web_frame = render_frame->GetWebFrame();
if (web_frame->IsWebLocalFrame()) {
web_frame->ToWebLocalFrame()->GetDocument().RemoveInsertedStyleSheet(
blink::WebString::FromUTF16(key));
}
}
}
v8::Local<v8::Promise> ExecuteJavaScript(gin_helper::Arguments* args,
v8::Local<v8::Value> window,
v8::Local<v8::Promise> ExecuteJavaScript(gin::Arguments* gin_args,
const std::u16string& code) {
gin_helper::Arguments* args = static_cast<gin_helper::Arguments*>(gin_args);
v8::Isolate* isolate = args->isolate();
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
auto* render_frame = GetRenderFrame(window);
if (!render_frame) {
promise.RejectWithErrorMessage(
"Render frame was torn down before webFrame.executeJavaScript could be "
"executed");
content::RenderFrame* render_frame;
std::string error_msg;
if (!MaybeGetRenderFrame(&error_msg, "executeJavaScript", &render_frame)) {
promise.RejectWithErrorMessage(error_msg);
return handle;
}
@ -643,22 +695,23 @@ v8::Local<v8::Promise> ExecuteJavaScript(gin_helper::Arguments* args,
std::move(completion_callback)));
return handle;
}
}
v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
gin_helper::Arguments* args,
v8::Local<v8::Value> window,
v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
gin::Arguments* gin_args,
int world_id,
const std::vector<gin_helper::Dictionary>& scripts) {
gin_helper::Arguments* args = static_cast<gin_helper::Arguments*>(gin_args);
v8::Isolate* isolate = args->isolate();
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
auto* render_frame = GetRenderFrame(window);
if (!render_frame) {
promise.RejectWithErrorMessage(
"Render frame was torn down before "
"webFrame.executeJavaScriptInIsolatedWorld could be executed");
content::RenderFrame* render_frame;
std::string error_msg;
if (!MaybeGetRenderFrame(&error_msg, "executeJavaScriptInIsolatedWorld",
&render_frame)) {
promise.RejectWithErrorMessage(error_msg);
return handle;
}
@ -706,27 +759,22 @@ v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
std::move(completion_callback)));
return handle;
}
void SetIsolatedWorldInfo(v8::Local<v8::Value> 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;
}
void SetIsolatedWorldInfo(v8::Isolate* isolate,
int world_id,
const gin_helper::Dictionary& options) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "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()) {
args->ThrowError(
gin_helper::ErrorThrower(isolate).ThrowError(
"If csp is specified, securityOrigin should also be specified");
return;
}
@ -737,154 +785,180 @@ void SetIsolatedWorldInfo(v8::Local<v8::Value> window,
info.content_security_policy = blink::WebString::FromUTF8(security_policy);
info.human_readable_name = blink::WebString::FromUTF8(name);
blink::SetIsolatedWorldInfo(world_id, info);
}
}
blink::WebCacheResourceTypeStats GetResourceUsage(v8::Isolate* isolate) {
blink::WebCacheResourceTypeStats GetResourceUsage(v8::Isolate* isolate) {
blink::WebCacheResourceTypeStats stats;
blink::WebCache::GetResourceTypeStats(&stats);
return stats;
}
}
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
bool IsWordMisspelled(v8::Isolate* isolate, const std::string& word) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "isWordMisspelled", &render_frame))
return false;
bool IsWordMisspelled(v8::Isolate* isolate,
v8::Local<v8::Value> window,
const std::string& word) {
return !SpellCheckWord(isolate, window, word, nullptr);
}
return !SpellCheckWord(render_frame, word, nullptr);
}
std::vector<std::u16string> GetWordSuggestions(v8::Isolate* isolate,
v8::Local<v8::Value> window,
std::vector<std::u16string> GetWordSuggestions(v8::Isolate* isolate,
const std::string& word) {
content::RenderFrame* render_frame;
std::vector<std::u16string> suggestions;
SpellCheckWord(isolate, window, word, &suggestions);
if (!MaybeGetRenderFrame(isolate, "getWordSuggestions", &render_frame))
return suggestions;
}
SpellCheckWord(render_frame, word, &suggestions);
return suggestions;
}
#endif
void ClearCache(v8::Isolate* isolate) {
void ClearCache(v8::Isolate* isolate) {
isolate->IdleNotificationDeadline(0.5);
blink::WebCache::Clear();
base::MemoryPressureListener::NotifyMemoryPressure(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
}
}
v8::Local<v8::Value> FindFrameByRoutingId(v8::Isolate* isolate,
v8::Local<v8::Value> window,
v8::Local<v8::Value> FindFrameByRoutingId(v8::Isolate* isolate,
int routing_id) {
content::RenderFrame* render_frame =
content::RenderFrame::FromRoutingID(routing_id);
if (render_frame)
return render_frame->GetWebFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(isolate, render_frame).ToV8();
else
return v8::Null(isolate);
}
}
v8::Local<v8::Value> GetOpener(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
auto* render_frame = GetRenderFrame(window);
if (!render_frame)
v8::Local<v8::Value> GetOpener(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "opener", &render_frame))
return v8::Null(isolate);
blink::WebFrame* frame = render_frame->GetWebFrame()->Opener();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
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<v8::Value> GetFrameParent(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Parent();
// Don't name it as GetParent, Windows has API with same name.
v8::Local<v8::Value> GetFrameParent(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "parent", &render_frame))
return v8::Null(isolate);
blink::WebFrame* frame = render_frame->GetWebFrame()->Parent();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(
isolate,
content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate);
}
}
v8::Local<v8::Value> GetTop(v8::Isolate* isolate, v8::Local<v8::Value> window) {
auto* render_frame = GetRenderFrame(window);
if (!render_frame)
v8::Local<v8::Value> GetTop(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "top", &render_frame))
return v8::Null(isolate);
blink::WebFrame* frame = render_frame->GetWebFrame()->Top();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(
isolate,
content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate);
}
}
v8::Local<v8::Value> GetFirstChild(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
auto* render_frame = GetRenderFrame(window);
if (!render_frame)
v8::Local<v8::Value> GetFirstChild(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "firstChild", &render_frame))
return v8::Null(isolate);
blink::WebFrame* frame = render_frame->GetWebFrame()->FirstChild();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(
isolate,
content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate);
}
}
v8::Local<v8::Value> GetNextSibling(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
auto* render_frame = GetRenderFrame(window);
if (!render_frame)
v8::Local<v8::Value> GetNextSibling(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "nextSibling", &render_frame))
return v8::Null(isolate);
blink::WebFrame* frame = render_frame->GetWebFrame()->NextSibling();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(
isolate,
content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate);
}
}
v8::Local<v8::Value> GetFrameForSelector(v8::Isolate* isolate,
v8::Local<v8::Value> window,
v8::Local<v8::Value> GetFrameForSelector(v8::Isolate* isolate,
const std::string& selector) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "getFrameForSelector", &render_frame))
return v8::Null(isolate);
blink::WebElement element =
GetRenderFrame(window)->GetWebFrame()->GetDocument().QuerySelector(
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 frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(
isolate,
content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate);
}
}
v8::Local<v8::Value> FindFrameByName(v8::Isolate* isolate,
v8::Local<v8::Value> window,
v8::Local<v8::Value> FindFrameByName(v8::Isolate* isolate,
const std::string& name) {
auto* render_frame = GetRenderFrame(window);
if (!render_frame)
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "getFrameForSelector", &render_frame))
return v8::Null(isolate);
blink::WebFrame* frame = render_frame->GetWebFrame()->FindFrameByName(
blink::WebString::FromUTF8(name));
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
return WebFrameRenderer::Create(
isolate,
content::RenderFrame::FromWebFrame(frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate);
}
int GetRoutingId(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> window) {
auto* render_frame = GetRenderFrame(window);
if (!render_frame) {
thrower.ThrowError(
"Render frame was torn down before webFrame.getRoutingId could be "
"executed");
return 0;
}
int GetRoutingId(v8::Isolate* isolate) {
content::RenderFrame* render_frame;
if (!MaybeGetRenderFrame(isolate, "routingId", &render_frame))
return 0;
return render_frame->GetRoutingID();
}
}
};
gin::WrapperInfo WebFrameRenderer::kWrapperInfo = {gin::kEmbedderNativeGin};
// static
std::set<SpellCheckerHolder*> SpellCheckerHolder::instances_;
} // namespace api
@ -900,39 +974,8 @@ void Initialize(v8::Local<v8::Object> 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

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 = {