blob: 5e66f0fcba577edb7e7c17e37b1e5a43a5f9d436 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/renderer/webui_browser/webui_browser_renderer_extension.h"
#include "base/check.h"
#include "chrome/common/url_constants.h"
#include "components/guest_contents/renderer/swap_render_frame.h"
#include "content/public/common/isolated_world_ids.h"
#include "gin/converter.h"
#include "gin/object_template_builder.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/web/web_custom_element.h"
#include "third_party/blink/public/web/web_document.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "v8/include/v8-context.h"
#include "v8/include/v8-function.h"
#include "v8/include/v8-isolate.h"
#include "v8/include/v8-local-handle.h"
namespace {
// Expose the API to only chrome://webui-browser/*.
bool ShouldExposeWebUIBrowserApi(content::RenderFrame* render_frame) {
CHECK(render_frame);
const url::Origin webui_browser_origin =
url::Origin::Create(GURL(chrome::kChromeUIWebuiBrowserURL));
return url::Origin::Create(render_frame->GetWebFrame()->GetDocument().Url())
.IsSameOriginWith(webui_browser_origin);
}
// Implementation of chrome.browser.allowCustomElementRegistration(callback)
void AllowCustomElementNameRegistration(v8::Local<v8::Function> callback) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope;
callback->Call(context, context->Global(), 0, nullptr).ToLocalChecked();
}
content::RenderFrame* GetRenderFrame(v8::Local<v8::Value> value) {
v8::Local<v8::Context> context;
if (!v8::Local<v8::Object>::Cast(value)->GetCreationContext().ToLocal(
&context)) {
if (context.IsEmpty()) {
return nullptr;
}
}
blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForContext(context);
if (!frame) {
return nullptr;
}
return content::RenderFrame::FromWebFrame(frame);
}
// Implementation of
// chrome.browser.attachIframeGuest(guestInstanceId, contentWindow)
void AttachIframeGuest(int guest_contents_id,
v8::Local<v8::Object> content_window) {
content::RenderFrame* iframe_render_frame = GetRenderFrame(content_window);
CHECK(iframe_render_frame);
blink::WebFrame* parent_frame = iframe_render_frame->GetWebFrame()->Parent();
CHECK(parent_frame);
CHECK(parent_frame->IsWebLocalFrame());
guest_contents::renderer::SwapRenderFrame(iframe_render_frame,
guest_contents_id);
}
} // namespace
// static
void WebUIBrowserRendererExtension::Create(content::RenderFrame* frame) {
new WebUIBrowserRendererExtension(frame);
}
WebUIBrowserRendererExtension::WebUIBrowserRendererExtension(
content::RenderFrame* frame)
: content::RenderFrameObserver(frame) {}
WebUIBrowserRendererExtension::~WebUIBrowserRendererExtension() = default;
void WebUIBrowserRendererExtension::OnDestruct() {
delete this;
}
void WebUIBrowserRendererExtension::DidCreateScriptContext(
v8::Local<v8::Context> v8_context,
int32_t world_id) {
if (!render_frame() || world_id != content::ISOLATED_WORLD_ID_GLOBAL) {
return;
}
if (ShouldExposeWebUIBrowserApi(render_frame())) {
InjectScript();
}
}
void WebUIBrowserRendererExtension::InjectScript() {
v8::Isolate* isolate =
render_frame()->GetWebFrame()->GetAgentGroupScheduler()->Isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context =
render_frame()->GetWebFrame()->MainWorldScriptContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::Object> global = context->Global();
v8::Local<v8::Value> chrome_value;
if (!global->Get(context, gin::StringToV8(isolate, "chrome"))
.ToLocal(&chrome_value)) {
chrome_value = v8::Object::New(isolate);
global->Set(context, gin::StringToV8(isolate, "chrome"), chrome_value)
.Check();
}
CHECK(chrome_value->IsObject());
v8::Local<v8::Object> chrome_obj = v8::Local<v8::Object>::Cast(chrome_value);
v8::Local<v8::ObjectTemplate> browser_template =
gin::ObjectTemplateBuilder(isolate)
.SetMethod("allowCustomElementRegistration",
&AllowCustomElementNameRegistration)
.SetMethod("attachIframeGuest", &AttachIframeGuest)
.Build();
chrome_obj
->Set(context, gin::StringToV8(isolate, "browser"),
browser_template->NewInstance(context).ToLocalChecked())
.FromJust();
}