Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Side by Side Diff: headless/lib/renderer/headless_content_renderer_client.cc

Issue 2873283002: [Reland] Allow headless TabSocket in isolated worlds & remove obsolete logic (Closed)
Patch Set: More tests Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "headless/lib/renderer/headless_content_renderer_client.h" 5 #include "headless/lib/renderer/headless_content_renderer_client.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "content/public/common/bindings_policy.h" 9 #include "content/public/common/bindings_policy.h"
10 #include "content/public/common/isolated_world_ids.h"
10 #include "content/public/renderer/render_frame.h" 11 #include "content/public/renderer/render_frame.h"
12 #include "content/public/renderer/render_frame_observer.h"
13 #include "gin/handle.h"
14 #include "gin/object_template_builder.h"
15 #include "gin/wrappable.h"
16 #include "headless/lib/tab_socket.mojom.h"
11 #include "printing/features/features.h" 17 #include "printing/features/features.h"
18 #include "services/service_manager/public/cpp/interface_provider.h"
19 #include "third_party/WebKit/public/web/WebKit.h"
12 20
13 #if BUILDFLAG(ENABLE_BASIC_PRINTING) 21 #if BUILDFLAG(ENABLE_BASIC_PRINTING)
14 #include "components/printing/renderer/print_web_view_helper.h" 22 #include "components/printing/renderer/print_web_view_helper.h"
15 #include "headless/lib/renderer/headless_print_web_view_helper_delegate.h" 23 #include "headless/lib/renderer/headless_print_web_view_helper_delegate.h"
16 #endif 24 #endif
17 25
18 namespace headless { 26 namespace headless {
19 27
20 HeadlessContentRendererClient::HeadlessContentRendererClient() {} 28 HeadlessContentRendererClient::HeadlessContentRendererClient() {}
21 29
22 HeadlessContentRendererClient::~HeadlessContentRendererClient() {} 30 HeadlessContentRendererClient::~HeadlessContentRendererClient() {}
23 31
32 // Keep in sync with DOMWrapperWorld::WorldId.
33 enum {
34 kDevToolsFirstIsolatedWorldId = (1 << 29) + 1,
jochen (gone - plz use gerrit) 2017/05/15 05:41:56 can you pull those IDs into a separate header, so
alex clarke (OOO till 29th) 2017/05/15 09:50:57 It would be nice if we could share the same header
35 kDevToolsLastIsolatedWorldId = kDevToolsFirstIsolatedWorldId + 100,
36 };
37
38 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate,
jochen (gone - plz use gerrit) 2017/05/15 05:41:56 that's gin::Converter<std::string>::ToV8()
alex clarke (OOO till 29th) 2017/05/15 09:50:57 Done.
39 const std::string& s) {
40 DCHECK(base::IsStringASCII(s));
41 return v8::String::NewFromUtf8(isolate, s.data(), v8::NewStringType::kNormal,
42 s.size())
43 .ToLocalChecked();
44 }
45
46 class HeadlessIsolatedWorldTabSocketBindings
Sami 2017/05/12 17:18:02 s/Isolated// since this is also used for the main
alex clarke (OOO till 29th) 2017/05/15 09:50:57 Good point, this was originally isolated only but
47 : public gin::Wrappable<HeadlessIsolatedWorldTabSocketBindings>,
48 public content::RenderFrameObserver {
49 public:
50 explicit HeadlessIsolatedWorldTabSocketBindings(
51 content::RenderFrame* render_frame)
52 : content::RenderFrameObserver(render_frame) {}
53 ~HeadlessIsolatedWorldTabSocketBindings() override {}
54
55 // content::RenderFrameObserver implementation:
56 void DidCreateScriptContext(v8::Local<v8::Context> context,
57 int world_id) override {
58 if (world_id == content::ISOLATED_WORLD_ID_GLOBAL) {
59 // For the main world only inject TabSocket if
60 // BINDINGS_POLICY_HEADLESS_MAIN_WORLD is set.
61 if (!(render_frame()->GetEnabledBindings() &
62 content::BindingsPolicy::BINDINGS_POLICY_HEADLESS_MAIN_WORLD)) {
63 return;
64 }
65 } else {
66 // For the isolated worlds only inject TabSocket if
67 // BINDINGS_POLICY_HEADLESS_ISOLATED_WORLD is set and the world id falls
68 // within the range reserved for DevTools created isolated worlds.
69 if (!(render_frame()->GetEnabledBindings() &
70 content::BindingsPolicy::BINDINGS_POLICY_HEADLESS_ISOLATED_WORLD)) {
71 return;
72 }
73 if (world_id < kDevToolsFirstIsolatedWorldId ||
74 world_id > kDevToolsLastIsolatedWorldId) {
75 return;
76 }
77 }
78
79 InitializeTabSocketBindings(context);
80 }
81
82 void OnDestruct() override {}
83
84 // gin::WrappableBase implementation:
85 gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
86 v8::Isolate* isolate) override {
87 return gin::Wrappable<HeadlessIsolatedWorldTabSocketBindings>::
88 GetObjectTemplateBuilder(isolate)
89 .SetMethod("send",
90 &HeadlessIsolatedWorldTabSocketBindings::SendImpl)
91 .SetProperty("onmessage",
92 &HeadlessIsolatedWorldTabSocketBindings::GetOnMessage,
93 &HeadlessIsolatedWorldTabSocketBindings::SetOnMessage);
94 }
95
96 static gin::WrapperInfo kWrapperInfo;
97
98 private:
99 void SendImpl(const std::string& message) {
100 EnsureTabSocketPtr()->SendMessageToEmbedder(message);
101 }
102
103 v8::Local<v8::Value> GetOnMessage() { return GetOnMessageCallback(); }
104
105 void SetOnMessage(v8::Local<v8::Function> callback) {
106 GetOnMessageCallback_.Reset(blink::MainThreadIsolate(), callback);
107
108 EnsureTabSocketPtr()->AwaitNextMessageFromEmbedder(base::Bind(
109 &HeadlessIsolatedWorldTabSocketBindings::OnNextMessageFromEmbedder,
110 base::Unretained(this)));
111 }
112
113 void OnNextMessageFromEmbedder(const std::string& message) {
114 CallOnMessageCallback(message);
115 }
116
117 v8::Local<v8::Value> CallOnMessageCallback(const std::string& message) {
118 v8::Isolate* isolate = blink::MainThreadIsolate();
119 v8::HandleScope handle_scope(isolate);
120 v8::Local<v8::Context> context = GetContext();
121 v8::Context::Scope context_scope(context);
122 v8::Local<v8::Value> argv[] = {
123 ASCIIStringToV8String(isolate, message),
Sami 2017/05/12 17:18:02 Hmm, is the message really guaranteed to ASCII?
alex clarke (OOO till 29th) 2017/05/15 09:50:57 That's the api contract.
124 };
125 v8::TryCatch try_catch(isolate);
126 v8::MicrotasksScope microtasks(isolate,
127 v8::MicrotasksScope::kDoNotRunMicrotasks);
128
129 v8::MaybeLocal<v8::Value> maybe_result = GetOnMessageCallback()->Call(
130 context, context->Global(), arraysize(argv), argv);
131
132 v8::Local<v8::Value> result;
133 if (!maybe_result.ToLocal(&result))
134 return v8::Local<v8::Value>();
135 return result;
136 }
137
138 void InitializeTabSocketBindings(v8::Local<v8::Context> context) {
139 // Add TabSocket bindings to the DevTools created isolated world.
140 v8::Isolate* isolate = blink::MainThreadIsolate();
141 v8::HandleScope handle_scope(isolate);
142 if (context.IsEmpty())
143 return;
144
145 v8::Context::Scope context_scope(context);
146 gin::Handle<HeadlessIsolatedWorldTabSocketBindings> bindings =
147 gin::CreateHandle(isolate, this);
148 if (bindings.IsEmpty())
149 return;
150
151 v8::Local<v8::Object> global = context->Global();
152 global->Set(gin::StringToV8(isolate, "TabSocket"), bindings.ToV8());
153 context_.Reset(blink::MainThreadIsolate(), context);
jochen (gone - plz use gerrit) 2017/05/15 05:41:56 this will create a memory leak, as the context now
alex clarke (OOO till 29th) 2017/05/15 09:50:57 I based this on what mojo does (see gin::ContextHo
154 }
155
156 headless::TabSocketPtr& EnsureTabSocketPtr() {
157 if (!tab_socket_ptr_.is_bound()) {
158 render_frame()->GetRemoteInterfaces()->GetInterface(
159 mojo::MakeRequest(&tab_socket_ptr_));
160 }
161 return tab_socket_ptr_;
162 }
163
164 v8::Local<v8::Context> GetContext() {
165 return v8::Local<v8::Context>::New(blink::MainThreadIsolate(), context_);
166 }
167
168 v8::Local<v8::Function> GetOnMessageCallback() {
169 return v8::Local<v8::Function>::New(blink::MainThreadIsolate(),
170 GetOnMessageCallback_);
171 }
172
173 headless::TabSocketPtr tab_socket_ptr_;
174 v8::UniquePersistent<v8::Context> context_;
175 v8::UniquePersistent<v8::Function> GetOnMessageCallback_;
176 };
177
178 gin::WrapperInfo HeadlessIsolatedWorldTabSocketBindings::kWrapperInfo = {
179 gin::kEmbedderNativeGin};
180
24 void HeadlessContentRendererClient::RenderFrameCreated( 181 void HeadlessContentRendererClient::RenderFrameCreated(
25 content::RenderFrame* render_frame) { 182 content::RenderFrame* render_frame) {
26 #if BUILDFLAG(ENABLE_BASIC_PRINTING) 183 #if BUILDFLAG(ENABLE_BASIC_PRINTING)
27 new printing::PrintWebViewHelper( 184 new printing::PrintWebViewHelper(
28 render_frame, base::MakeUnique<HeadlessPrintWebViewHelperDelegate>()); 185 render_frame, base::MakeUnique<HeadlessPrintWebViewHelperDelegate>());
29 #endif 186 #endif
30 } 187 new HeadlessIsolatedWorldTabSocketBindings(render_frame);
31
32 void HeadlessContentRendererClient::RunScriptsAtDocumentStart(
33 content::RenderFrame* render_frame) {
34 if (!(render_frame->GetEnabledBindings() &
35 content::BindingsPolicy::BINDINGS_POLICY_HEADLESS)) {
36 return;
37 }
38
39 render_frame->ExecuteJavaScript(base::UTF8ToUTF16(R"(
40 (function() {
41 let tabSocket = null;
42 let messagesToSend = [];
43
44 function getNextEmbedderMessage() {
45 tabSocket.awaitNextMessageFromEmbedder().then(
46 function(result) {
47 window.TabSocket.onmessage(new CustomEvent(
48 "onmessage", {detail : {message : result.message}}));
49 getNextEmbedderMessage();
50 });
51 };
52
53 window.TabSocket = new Proxy(
54 {
55 send: function(message) {
56 if (tabSocket) {
57 tabSocket.sendMessageToEmbedder(message);
58 } else {
59 messagesToSend.push(message);
60 }
61 },
62 onmessage: null
63 },
64 {
65 set: function(target, prop, value, receiver) {
66 if (prop !== "onmessage")
67 return false;
68 target[prop] = value;
69 if (tabSocket)
70 getNextEmbedderMessage();
71 return true;
72 }
73 });
74
75 // Note define() defines a module in the mojo module dependency
76 // system. While we don't expose our module, the callback below only
77 // fires after the requested modules have been loaded.
78 define([
79 'headless/lib/tab_socket.mojom',
80 'content/public/renderer/frame_interfaces',
81 ], function(tabSocketMojom, frameInterfaces) {
82 tabSocket = new tabSocketMojom.TabSocketPtr(
83 frameInterfaces.getInterface(tabSocketMojom.TabSocket.name));
84 // Send any messages that may have been created before the dependency
85 // was resolved.
86 for (var i = 0; i < messagesToSend.length; i++) {
87 tabSocket.sendMessageToEmbedder(messagesToSend[i]);
88 }
89 messagesToSend = [];
90 if (window.TabSocket.onmessage)
91 getNextEmbedderMessage();
92 });
93
94 })(); )"));
95 } 188 }
96 189
97 } // namespace headless 190 } // namespace headless
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698