Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / renderer / guest_view / mime_handler_view / mime_handler_view_container.cc
blob7d34c7593fd543e3aac9af7f4ea2c21f9c1fdb9f
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
7 #include <map>
8 #include <set>
10 #include "components/guest_view/common/guest_view_constants.h"
11 #include "components/guest_view/common/guest_view_messages.h"
12 #include "content/public/child/v8_value_converter.h"
13 #include "content/public/renderer/render_frame.h"
14 #include "content/public/renderer/render_view.h"
15 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h"
16 #include "extensions/common/extension_messages.h"
17 #include "extensions/common/guest_view/extensions_guest_view_messages.h"
18 #include "gin/arguments.h"
19 #include "gin/dictionary.h"
20 #include "gin/handle.h"
21 #include "gin/interceptor.h"
22 #include "gin/object_template_builder.h"
23 #include "gin/wrappable.h"
24 #include "third_party/WebKit/public/web/WebDocument.h"
25 #include "third_party/WebKit/public/web/WebLocalFrame.h"
26 #include "third_party/WebKit/public/web/WebRemoteFrame.h"
27 #include "third_party/WebKit/public/web/WebView.h"
29 namespace extensions {
31 namespace {
33 const char kPostMessageName[] = "postMessage";
35 // The gin-backed scriptable object which is exposed by the BrowserPlugin for
36 // MimeHandlerViewContainer. This currently only implements "postMessage".
37 class ScriptableObject : public gin::Wrappable<ScriptableObject>,
38 public gin::NamedPropertyInterceptor {
39 public:
40 static gin::WrapperInfo kWrapperInfo;
42 static v8::Local<v8::Object> Create(
43 v8::Isolate* isolate,
44 base::WeakPtr<MimeHandlerViewContainer> container) {
45 ScriptableObject* scriptable_object =
46 new ScriptableObject(isolate, container);
47 return gin::CreateHandle(isolate, scriptable_object)
48 .ToV8()
49 .As<v8::Object>();
52 // gin::NamedPropertyInterceptor
53 v8::Local<v8::Value> GetNamedProperty(
54 v8::Isolate* isolate,
55 const std::string& identifier) override {
56 if (identifier == kPostMessageName) {
57 if (post_message_function_template_.IsEmpty()) {
58 post_message_function_template_.Reset(
59 isolate,
60 gin::CreateFunctionTemplate(
61 isolate, base::Bind(&MimeHandlerViewContainer::PostMessage,
62 container_, isolate)));
64 v8::Local<v8::FunctionTemplate> function_template =
65 v8::Local<v8::FunctionTemplate>::New(isolate,
66 post_message_function_template_);
67 v8::Local<v8::Function> function;
68 if (function_template->GetFunction(isolate->GetCurrentContext())
69 .ToLocal(&function))
70 return function;
72 return v8::Local<v8::Value>();
75 private:
76 ScriptableObject(v8::Isolate* isolate,
77 base::WeakPtr<MimeHandlerViewContainer> container)
78 : gin::NamedPropertyInterceptor(isolate, this),
79 container_(container) {}
81 // gin::Wrappable
82 gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
83 v8::Isolate* isolate) override {
84 return gin::Wrappable<ScriptableObject>::GetObjectTemplateBuilder(isolate)
85 .AddNamedPropertyInterceptor();
88 base::WeakPtr<MimeHandlerViewContainer> container_;
89 v8::Persistent<v8::FunctionTemplate> post_message_function_template_;
92 // static
93 gin::WrapperInfo ScriptableObject::kWrapperInfo = { gin::kEmbedderNativeGin };
95 // Maps from content::RenderFrame to the set of MimeHandlerViewContainers within
96 // it.
97 base::LazyInstance<
98 std::map<content::RenderFrame*, std::set<MimeHandlerViewContainer*>>>
99 g_mime_handler_view_container_map = LAZY_INSTANCE_INITIALIZER;
101 } // namespace
103 MimeHandlerViewContainer::MimeHandlerViewContainer(
104 content::RenderFrame* render_frame,
105 const std::string& mime_type,
106 const GURL& original_url)
107 : GuestViewContainer(render_frame),
108 mime_type_(mime_type),
109 original_url_(original_url),
110 guest_proxy_routing_id_(-1),
111 guest_loaded_(false),
112 weak_factory_(this) {
113 DCHECK(!mime_type_.empty());
114 is_embedded_ = !render_frame->GetWebFrame()->document().isPluginDocument();
115 g_mime_handler_view_container_map.Get()[render_frame].insert(this);
118 MimeHandlerViewContainer::~MimeHandlerViewContainer() {
119 if (loader_)
120 loader_->cancel();
122 if (render_frame()) {
123 g_mime_handler_view_container_map.Get()[render_frame()].erase(this);
124 if (g_mime_handler_view_container_map.Get()[render_frame()].empty())
125 g_mime_handler_view_container_map.Get().erase(render_frame());
129 // static
130 std::vector<MimeHandlerViewContainer*>
131 MimeHandlerViewContainer::FromRenderFrame(content::RenderFrame* render_frame) {
132 auto it = g_mime_handler_view_container_map.Get().find(render_frame);
133 if (it == g_mime_handler_view_container_map.Get().end())
134 return std::vector<MimeHandlerViewContainer*>();
136 return std::vector<MimeHandlerViewContainer*>(it->second.begin(),
137 it->second.end());
140 void MimeHandlerViewContainer::OnReady() {
141 if (!render_frame())
142 return;
144 blink::WebFrame* frame = render_frame()->GetWebFrame();
145 blink::WebURLLoaderOptions options;
146 // The embedded plugin is allowed to be cross-origin and we should always
147 // send credentials/cookies with the request.
148 options.crossOriginRequestPolicy =
149 blink::WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
150 options.allowCredentials = true;
151 DCHECK(!loader_);
152 loader_.reset(frame->createAssociatedURLLoader(options));
154 blink::WebURLRequest request(original_url_);
155 request.setRequestContext(blink::WebURLRequest::RequestContextObject);
156 loader_->loadAsynchronously(request, this);
159 bool MimeHandlerViewContainer::OnMessage(const IPC::Message& message) {
160 bool handled = true;
161 IPC_BEGIN_MESSAGE_MAP(MimeHandlerViewContainer, message)
162 IPC_MESSAGE_HANDLER(ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK,
163 OnCreateMimeHandlerViewGuestACK)
164 IPC_MESSAGE_HANDLER(
165 ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted,
166 OnMimeHandlerViewGuestOnLoadCompleted)
167 IPC_MESSAGE_HANDLER(GuestViewMsg_GuestAttached, OnGuestAttached)
168 IPC_MESSAGE_UNHANDLED(handled = false)
169 IPC_END_MESSAGE_MAP()
170 return handled;
173 void MimeHandlerViewContainer::DidFinishLoading() {
174 DCHECK(!is_embedded_);
175 CreateMimeHandlerViewGuest();
178 void MimeHandlerViewContainer::OnRenderFrameDestroyed() {
179 g_mime_handler_view_container_map.Get().erase(render_frame());
182 void MimeHandlerViewContainer::DidReceiveData(const char* data,
183 int data_length) {
184 view_id_ += std::string(data, data_length);
188 void MimeHandlerViewContainer::DidResizeElement(const gfx::Size& new_size) {
189 element_size_ = new_size;
190 render_frame()->Send(new ExtensionsGuestViewHostMsg_ResizeGuest(
191 render_frame()->GetRoutingID(), element_instance_id(), new_size));
194 v8::Local<v8::Object> MimeHandlerViewContainer::V8ScriptableObject(
195 v8::Isolate* isolate) {
196 if (scriptable_object_.IsEmpty()) {
197 v8::Local<v8::Object> object =
198 ScriptableObject::Create(isolate, weak_factory_.GetWeakPtr());
199 scriptable_object_.Reset(isolate, object);
201 return v8::Local<v8::Object>::New(isolate, scriptable_object_);
204 void MimeHandlerViewContainer::didReceiveData(blink::WebURLLoader* /* unused */,
205 const char* data,
206 int data_length,
207 int /* unused */) {
208 view_id_ += std::string(data, data_length);
211 void MimeHandlerViewContainer::didFinishLoading(
212 blink::WebURLLoader* /* unused */,
213 double /* unused */,
214 int64_t /* unused */) {
215 DCHECK(is_embedded_);
216 CreateMimeHandlerViewGuest();
219 void MimeHandlerViewContainer::PostMessage(v8::Isolate* isolate,
220 v8::Local<v8::Value> message) {
221 if (!guest_loaded_) {
222 linked_ptr<v8::Global<v8::Value>> global(
223 new v8::Global<v8::Value>(isolate, message));
224 pending_messages_.push_back(global);
225 return;
228 content::RenderView* guest_proxy_render_view =
229 content::RenderView::FromRoutingID(guest_proxy_routing_id_);
230 if (!guest_proxy_render_view)
231 return;
232 blink::WebFrame* guest_proxy_frame =
233 guest_proxy_render_view->GetWebView()->mainFrame();
234 if (!guest_proxy_frame)
235 return;
237 v8::Context::Scope context_scope(
238 render_frame()->GetWebFrame()->mainWorldScriptContext());
240 // TODO(lazyboy,nasko): The WebLocalFrame branch is not used when running
241 // on top of out-of-process iframes. Remove it once the code is converted.
242 v8::Local<v8::Object> guest_proxy_window;
243 if (guest_proxy_frame->isWebLocalFrame()) {
244 guest_proxy_window =
245 guest_proxy_frame->mainWorldScriptContext()->Global();
246 } else {
247 guest_proxy_window = guest_proxy_frame->toWebRemoteFrame()
248 ->deprecatedMainWorldScriptContext()
249 ->Global();
251 gin::Dictionary window_object(isolate, guest_proxy_window);
252 v8::Local<v8::Function> post_message;
253 if (!window_object.Get(std::string(kPostMessageName), &post_message))
254 return;
256 v8::Local<v8::Value> args[] = {
257 message,
258 // Post the message to any domain inside the browser plugin. The embedder
259 // should already know what is embedded.
260 gin::StringToV8(isolate, "*")};
261 render_frame()->GetWebFrame()->callFunctionEvenIfScriptDisabled(
262 post_message.As<v8::Function>(),
263 guest_proxy_window,
264 arraysize(args),
265 args);
268 void MimeHandlerViewContainer::PostMessageFromValue(
269 const base::Value& message) {
270 blink::WebFrame* frame = render_frame()->GetWebFrame();
271 if (!frame)
272 return;
274 v8::Isolate* isolate = v8::Isolate::GetCurrent();
275 v8::HandleScope handle_scope(isolate);
276 v8::Context::Scope context_scope(frame->mainWorldScriptContext());
277 scoped_ptr<content::V8ValueConverter> converter(
278 content::V8ValueConverter::create());
279 PostMessage(isolate,
280 converter->ToV8Value(&message, frame->mainWorldScriptContext()));
283 void MimeHandlerViewContainer::OnCreateMimeHandlerViewGuestACK(
284 int element_instance_id) {
285 DCHECK_NE(this->element_instance_id(), guest_view::kInstanceIDNone);
286 DCHECK_EQ(this->element_instance_id(), element_instance_id);
288 if (!render_frame())
289 return;
291 render_frame()->AttachGuest(element_instance_id);
294 void MimeHandlerViewContainer::OnGuestAttached(int /* unused */,
295 int guest_proxy_routing_id) {
296 // Save the RenderView routing ID of the guest here so it can be used to route
297 // PostMessage calls.
298 guest_proxy_routing_id_ = guest_proxy_routing_id;
301 void MimeHandlerViewContainer::OnMimeHandlerViewGuestOnLoadCompleted(
302 int /* unused */) {
303 if (!render_frame())
304 return;
306 guest_loaded_ = true;
307 if (pending_messages_.empty())
308 return;
310 // Now that the guest has loaded, flush any unsent messages.
311 blink::WebFrame* frame = render_frame()->GetWebFrame();
312 if (!frame)
313 return;
315 v8::Isolate* isolate = v8::Isolate::GetCurrent();
316 v8::HandleScope handle_scope(isolate);
317 v8::Context::Scope context_scope(frame->mainWorldScriptContext());
318 for (const auto& pending_message : pending_messages_)
319 PostMessage(isolate, v8::Local<v8::Value>::New(isolate, *pending_message));
321 pending_messages_.clear();
324 void MimeHandlerViewContainer::CreateMimeHandlerViewGuest() {
325 // The loader has completed loading |view_id_| so we can dispose it.
326 loader_.reset();
328 DCHECK_NE(element_instance_id(), guest_view::kInstanceIDNone);
330 if (!render_frame())
331 return;
333 render_frame()->Send(
334 new ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest(
335 render_frame()->GetRoutingID(), view_id_, element_instance_id(),
336 element_size_));
339 } // namespace extensions