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"
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
{
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
{
40 static gin::WrapperInfo kWrapperInfo
;
42 static v8::Local
<v8::Object
> Create(
44 base::WeakPtr
<MimeHandlerViewContainer
> container
) {
45 ScriptableObject
* scriptable_object
=
46 new ScriptableObject(isolate
, container
);
47 return gin::CreateHandle(isolate
, scriptable_object
)
52 // gin::NamedPropertyInterceptor
53 v8::Local
<v8::Value
> GetNamedProperty(
55 const std::string
& identifier
) override
{
56 if (identifier
== kPostMessageName
) {
57 if (post_message_function_template_
.IsEmpty()) {
58 post_message_function_template_
.Reset(
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())
72 return v8::Local
<v8::Value
>();
76 ScriptableObject(v8::Isolate
* isolate
,
77 base::WeakPtr
<MimeHandlerViewContainer
> container
)
78 : gin::NamedPropertyInterceptor(isolate
, this),
79 container_(container
) {}
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_
;
93 gin::WrapperInfo
ScriptableObject::kWrapperInfo
= { gin::kEmbedderNativeGin
};
95 // Maps from content::RenderFrame to the set of MimeHandlerViewContainers within
98 std::map
<content::RenderFrame
*, std::set
<MimeHandlerViewContainer
*>>>
99 g_mime_handler_view_container_map
= LAZY_INSTANCE_INITIALIZER
;
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() {
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());
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(),
140 void MimeHandlerViewContainer::OnReady() {
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;
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
) {
161 IPC_BEGIN_MESSAGE_MAP(MimeHandlerViewContainer
, message
)
162 IPC_MESSAGE_HANDLER(ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK
,
163 OnCreateMimeHandlerViewGuestACK
)
165 ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted
,
166 OnMimeHandlerViewGuestOnLoadCompleted
)
167 IPC_MESSAGE_HANDLER(GuestViewMsg_GuestAttached
, OnGuestAttached
)
168 IPC_MESSAGE_UNHANDLED(handled
= false)
169 IPC_END_MESSAGE_MAP()
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
,
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 */,
208 view_id_
+= std::string(data
, data_length
);
211 void MimeHandlerViewContainer::didFinishLoading(
212 blink::WebURLLoader
* /* 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
);
228 content::RenderView
* guest_proxy_render_view
=
229 content::RenderView::FromRoutingID(guest_proxy_routing_id_
);
230 if (!guest_proxy_render_view
)
232 blink::WebFrame
* guest_proxy_frame
=
233 guest_proxy_render_view
->GetWebView()->mainFrame();
234 if (!guest_proxy_frame
)
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()) {
245 guest_proxy_frame
->mainWorldScriptContext()->Global();
247 guest_proxy_window
= guest_proxy_frame
->toWebRemoteFrame()
248 ->deprecatedMainWorldScriptContext()
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
))
256 v8::Local
<v8::Value
> args
[] = {
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
>(),
268 void MimeHandlerViewContainer::PostMessageFromValue(
269 const base::Value
& message
) {
270 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
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());
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
);
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(
306 guest_loaded_
= true;
307 if (pending_messages_
.empty())
310 // Now that the guest has loaded, flush any unsent messages.
311 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
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.
328 DCHECK_NE(element_instance_id(), guest_view::kInstanceIDNone
);
333 render_frame()->Send(
334 new ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest(
335 render_frame()->GetRoutingID(), view_id_
, element_instance_id(),
339 } // namespace extensions