Index: headless/lib/renderer/headless_content_renderer_client.cc |
diff --git a/headless/lib/renderer/headless_content_renderer_client.cc b/headless/lib/renderer/headless_content_renderer_client.cc |
index b627f692601aabfc5bfe377a2261f2de20a02c89..68897ba9947525b837232c85da431fd46e39d7c6 100644 |
--- a/headless/lib/renderer/headless_content_renderer_client.cc |
+++ b/headless/lib/renderer/headless_content_renderer_client.cc |
@@ -7,8 +7,19 @@ |
#include "base/memory/ptr_util.h" |
#include "base/strings/utf_string_conversions.h" |
#include "content/public/common/bindings_policy.h" |
+#include "content/public/common/isolated_world_ids.h" |
#include "content/public/renderer/render_frame.h" |
+#include "content/public/renderer/render_frame_observer.h" |
+#include "gin/handle.h" |
+#include "gin/object_template_builder.h" |
+#include "gin/wrappable.h" |
+#include "headless/lib/tab_socket.mojom.h" |
#include "printing/features/features.h" |
+#include "services/service_manager/public/cpp/interface_provider.h" |
+#include "third_party/WebKit/public/web/WebIsolatedWorldIds.h" |
+#include "third_party/WebKit/public/web/WebKit.h" |
+#include "third_party/WebKit/public/web/WebLocalFrame.h" |
+#include "third_party/WebKit/public/web/WebScriptExecutionCallback.h" |
#if BUILDFLAG(ENABLE_BASIC_PRINTING) |
#include "components/printing/renderer/print_web_view_helper.h" |
@@ -21,77 +32,143 @@ HeadlessContentRendererClient::HeadlessContentRendererClient() {} |
HeadlessContentRendererClient::~HeadlessContentRendererClient() {} |
+class HeadlessTabSocketBindings |
+ : public gin::Wrappable<HeadlessTabSocketBindings>, |
+ public content::RenderFrameObserver, |
+ public blink::WebScriptExecutionCallback { |
+ public: |
+ explicit HeadlessTabSocketBindings(content::RenderFrame* render_frame) |
+ : content::RenderFrameObserver(render_frame), weak_ptr_factory_(this) {} |
+ ~HeadlessTabSocketBindings() override {} |
+ |
+ // content::RenderFrameObserver implementation: |
+ void DidCreateScriptContext(v8::Local<v8::Context> context, |
+ int world_id) override { |
+ if (world_id == content::ISOLATED_WORLD_ID_GLOBAL) { |
+ // For the main world only inject TabSocket if |
+ // BINDINGS_POLICY_HEADLESS_MAIN_WORLD is set. |
+ if (!(render_frame()->GetEnabledBindings() & |
+ content::BindingsPolicy::BINDINGS_POLICY_HEADLESS_MAIN_WORLD)) { |
+ return; |
+ } |
+ } else { |
+ // For the isolated worlds only inject TabSocket if |
+ // BINDINGS_POLICY_HEADLESS_ISOLATED_WORLD is set and the world id falls |
+ // within the range reserved for DevTools created isolated worlds. |
+ if (!(render_frame()->GetEnabledBindings() & |
+ content::BindingsPolicy::BINDINGS_POLICY_HEADLESS_ISOLATED_WORLD)) { |
+ return; |
+ } |
+ if (world_id < blink::kDevToolsFirstIsolatedWorldId || |
+ world_id > blink::kDevToolsLastIsolatedWorldId) { |
+ return; |
+ } |
+ } |
+ |
+ InitializeTabSocketBindings(context); |
+ } |
+ |
+ void WillReleaseScriptContext(v8::Local<v8::Context> context, |
+ int world_id) override { |
+ if (context_ == context) { |
+ on_message_callback_.Reset(); |
+ context_.Reset(); |
+ } |
+ } |
+ |
+ void OnDestruct() override {} |
+ |
+ // gin::WrappableBase implementation: |
+ gin::ObjectTemplateBuilder GetObjectTemplateBuilder( |
+ v8::Isolate* isolate) override { |
+ return gin::Wrappable<HeadlessTabSocketBindings>::GetObjectTemplateBuilder( |
+ isolate) |
+ .SetMethod("send", &HeadlessTabSocketBindings::SendImpl) |
+ .SetProperty("onmessage", &HeadlessTabSocketBindings::GetOnMessage, |
+ &HeadlessTabSocketBindings::SetOnMessage); |
+ } |
+ |
+ static gin::WrapperInfo kWrapperInfo; |
+ |
+ private: |
+ void SendImpl(const std::string& message) { |
+ EnsureTabSocketPtr()->SendMessageToEmbedder(message); |
+ } |
+ |
+ v8::Local<v8::Value> GetOnMessage() { return GetOnMessageCallback(); } |
+ |
+ void SetOnMessage(v8::Local<v8::Function> callback) { |
+ on_message_callback_.Reset(blink::MainThreadIsolate(), callback); |
+ |
+ EnsureTabSocketPtr()->AwaitNextMessageFromEmbedder( |
+ base::Bind(&HeadlessTabSocketBindings::OnNextMessageFromEmbedder, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ } |
+ |
+ void OnNextMessageFromEmbedder(const std::string& message) { |
+ v8::Isolate* isolate = blink::MainThreadIsolate(); |
+ v8::HandleScope handle_scope(isolate); |
+ v8::Local<v8::Context> context = GetContext(); |
+ v8::Local<v8::Value> argv[] = { |
+ gin::Converter<std::string>::ToV8(isolate, message), |
+ }; |
+ |
+ render_frame()->GetWebFrame()->RequestExecuteV8Function( |
+ context, GetOnMessageCallback(), context->Global(), arraysize(argv), |
+ argv, this); |
+ } |
+ |
+ void InitializeTabSocketBindings(v8::Local<v8::Context> context) { |
+ // Add TabSocket bindings to the DevTools created isolated world. |
+ v8::Isolate* isolate = blink::MainThreadIsolate(); |
+ v8::HandleScope handle_scope(isolate); |
+ if (context.IsEmpty()) |
+ return; |
+ |
+ v8::Context::Scope context_scope(context); |
+ gin::Handle<HeadlessTabSocketBindings> bindings = |
+ gin::CreateHandle(isolate, this); |
+ if (bindings.IsEmpty()) |
+ return; |
+ |
+ v8::Local<v8::Object> global = context->Global(); |
+ global->Set(gin::StringToV8(isolate, "TabSocket"), bindings.ToV8()); |
+ context_.Reset(blink::MainThreadIsolate(), context); |
+ } |
+ |
+ headless::TabSocketPtr& EnsureTabSocketPtr() { |
+ if (!tab_socket_ptr_.is_bound()) { |
+ render_frame()->GetRemoteInterfaces()->GetInterface( |
+ mojo::MakeRequest(&tab_socket_ptr_)); |
+ } |
+ return tab_socket_ptr_; |
+ } |
+ |
+ v8::Local<v8::Context> GetContext() { |
+ return v8::Local<v8::Context>::New(blink::MainThreadIsolate(), context_); |
+ } |
+ |
+ v8::Local<v8::Function> GetOnMessageCallback() { |
+ return v8::Local<v8::Function>::New(blink::MainThreadIsolate(), |
+ on_message_callback_); |
+ } |
+ |
+ headless::TabSocketPtr tab_socket_ptr_; |
+ v8::UniquePersistent<v8::Context> context_; |
+ v8::UniquePersistent<v8::Function> on_message_callback_; |
+ base::WeakPtrFactory<HeadlessTabSocketBindings> weak_ptr_factory_; |
+}; |
+ |
+gin::WrapperInfo HeadlessTabSocketBindings::kWrapperInfo = { |
+ gin::kEmbedderNativeGin}; |
+ |
void HeadlessContentRendererClient::RenderFrameCreated( |
content::RenderFrame* render_frame) { |
#if BUILDFLAG(ENABLE_BASIC_PRINTING) |
new printing::PrintWebViewHelper( |
render_frame, base::MakeUnique<HeadlessPrintWebViewHelperDelegate>()); |
#endif |
-} |
- |
-void HeadlessContentRendererClient::RunScriptsAtDocumentStart( |
- content::RenderFrame* render_frame) { |
- if (!(render_frame->GetEnabledBindings() & |
- content::BindingsPolicy::BINDINGS_POLICY_HEADLESS)) { |
- return; |
- } |
- |
- render_frame->ExecuteJavaScript(base::UTF8ToUTF16(R"( |
- (function() { |
- let tabSocket = null; |
- let messagesToSend = []; |
- |
- function getNextEmbedderMessage() { |
- tabSocket.awaitNextMessageFromEmbedder().then( |
- function(result) { |
- window.TabSocket.onmessage(new CustomEvent( |
- "onmessage", {detail : {message : result.message}})); |
- getNextEmbedderMessage(); |
- }); |
- }; |
- |
- window.TabSocket = new Proxy( |
- { |
- send: function(message) { |
- if (tabSocket) { |
- tabSocket.sendMessageToEmbedder(message); |
- } else { |
- messagesToSend.push(message); |
- } |
- }, |
- onmessage: null |
- }, |
- { |
- set: function(target, prop, value, receiver) { |
- if (prop !== "onmessage") |
- return false; |
- target[prop] = value; |
- if (tabSocket) |
- getNextEmbedderMessage(); |
- return true; |
- } |
- }); |
- |
- // Note define() defines a module in the mojo module dependency |
- // system. While we don't expose our module, the callback below only |
- // fires after the requested modules have been loaded. |
- define([ |
- 'headless/lib/tab_socket.mojom', |
- 'content/public/renderer/frame_interfaces', |
- ], function(tabSocketMojom, frameInterfaces) { |
- tabSocket = new tabSocketMojom.TabSocketPtr( |
- frameInterfaces.getInterface(tabSocketMojom.TabSocket.name)); |
- // Send any messages that may have been created before the dependency |
- // was resolved. |
- for (var i = 0; i < messagesToSend.length; i++) { |
- tabSocket.sendMessageToEmbedder(messagesToSend[i]); |
- } |
- messagesToSend = []; |
- if (window.TabSocket.onmessage) |
- getNextEmbedderMessage(); |
- }); |
- |
- })(); )")); |
+ new HeadlessTabSocketBindings(render_frame); |
} |
} // namespace headless |