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 "content/public/child/v8_value_converter.h"
11 #include "content/public/renderer/render_frame.h"
12 #include "content/public/renderer/render_view.h"
13 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h"
14 #include "extensions/common/extension_messages.h"
15 #include "extensions/common/guest_view/extensions_guest_view_messages.h"
16 #include "extensions/common/guest_view/guest_view_constants.h"
17 #include "extensions/common/guest_view/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/WebView.h"
28 namespace extensions
{
32 const char kPostMessageName
[] = "postMessage";
34 // The gin-backed scriptable object which is exposed by the BrowserPlugin for
35 // MimeHandlerViewContainer. This currently only implements "postMessage".
36 class ScriptableObject
: public gin::Wrappable
<ScriptableObject
>,
37 public gin::NamedPropertyInterceptor
{
39 static gin::WrapperInfo kWrapperInfo
;
41 static v8::Handle
<v8::Object
> Create(
43 base::WeakPtr
<MimeHandlerViewContainer
> container
) {
44 ScriptableObject
* scriptable_object
=
45 new ScriptableObject(isolate
, container
);
46 return gin::CreateHandle(isolate
, scriptable_object
).ToV8()->ToObject();
49 // gin::NamedPropertyInterceptor
50 v8::Local
<v8::Value
> GetNamedProperty(
52 const std::string
& identifier
) override
{
53 if (identifier
== kPostMessageName
) {
54 if (post_message_function_template_
.IsEmpty()) {
55 post_message_function_template_
.Reset(
57 gin::CreateFunctionTemplate(
58 isolate
, base::Bind(&MimeHandlerViewContainer::PostMessage
,
59 container_
, isolate
)));
61 return v8::Local
<v8::FunctionTemplate
>::New(
62 isolate
, post_message_function_template_
)->GetFunction();
64 return v8::Local
<v8::Value
>();
68 ScriptableObject(v8::Isolate
* isolate
,
69 base::WeakPtr
<MimeHandlerViewContainer
> container
)
70 : gin::NamedPropertyInterceptor(isolate
, this),
71 container_(container
) {}
74 gin::ObjectTemplateBuilder
GetObjectTemplateBuilder(
75 v8::Isolate
* isolate
) override
{
76 return gin::Wrappable
<ScriptableObject
>::GetObjectTemplateBuilder(isolate
)
77 .AddNamedPropertyInterceptor();
80 base::WeakPtr
<MimeHandlerViewContainer
> container_
;
81 v8::Persistent
<v8::FunctionTemplate
> post_message_function_template_
;
85 gin::WrapperInfo
ScriptableObject::kWrapperInfo
= { gin::kEmbedderNativeGin
};
87 // Maps from content::RenderFrame to the set of MimeHandlerViewContainers within
90 std::map
<content::RenderFrame
*, std::set
<MimeHandlerViewContainer
*>>>
91 g_mime_handler_view_container_map
= LAZY_INSTANCE_INITIALIZER
;
95 MimeHandlerViewContainer::MimeHandlerViewContainer(
96 content::RenderFrame
* render_frame
,
97 const std::string
& mime_type
,
98 const GURL
& original_url
)
99 : GuestViewContainer(render_frame
),
100 mime_type_(mime_type
),
101 original_url_(original_url
),
102 guest_proxy_routing_id_(-1),
103 guest_loaded_(false),
104 weak_factory_(this) {
105 DCHECK(!mime_type_
.empty());
106 is_embedded_
= !render_frame
->GetWebFrame()->document().isPluginDocument();
107 g_mime_handler_view_container_map
.Get()[render_frame
].insert(this);
110 MimeHandlerViewContainer::~MimeHandlerViewContainer() {
114 if (render_frame()) {
115 g_mime_handler_view_container_map
.Get()[render_frame()].erase(this);
116 if (g_mime_handler_view_container_map
.Get()[render_frame()].empty())
117 g_mime_handler_view_container_map
.Get().erase(render_frame());
122 std::vector
<MimeHandlerViewContainer
*>
123 MimeHandlerViewContainer::FromRenderFrame(content::RenderFrame
* render_frame
) {
124 auto it
= g_mime_handler_view_container_map
.Get().find(render_frame
);
125 if (it
== g_mime_handler_view_container_map
.Get().end())
126 return std::vector
<MimeHandlerViewContainer
*>();
128 return std::vector
<MimeHandlerViewContainer
*>(it
->second
.begin(),
132 void MimeHandlerViewContainer::Ready() {
136 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
137 blink::WebURLLoaderOptions options
;
138 // The embedded plugin is allowed to be cross-origin and we should always
139 // send credentials/cookies with the request.
140 options
.crossOriginRequestPolicy
=
141 blink::WebURLLoaderOptions::CrossOriginRequestPolicyAllow
;
142 options
.allowCredentials
= true;
144 loader_
.reset(frame
->createAssociatedURLLoader(options
));
146 blink::WebURLRequest
request(original_url_
);
147 request
.setRequestContext(blink::WebURLRequest::RequestContextObject
);
148 loader_
->loadAsynchronously(request
, this);
151 void MimeHandlerViewContainer::DidFinishLoading() {
152 DCHECK(!is_embedded_
);
153 CreateMimeHandlerViewGuest();
156 void MimeHandlerViewContainer::OnRenderFrameDestroyed() {
157 g_mime_handler_view_container_map
.Get().erase(render_frame());
160 void MimeHandlerViewContainer::DidReceiveData(const char* data
,
162 view_id_
+= std::string(data
, data_length
);
165 bool MimeHandlerViewContainer::OnMessageReceived(const IPC::Message
& message
) {
167 IPC_BEGIN_MESSAGE_MAP(MimeHandlerViewContainer
, message
)
168 IPC_MESSAGE_HANDLER(ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK
,
169 OnCreateMimeHandlerViewGuestACK
)
171 ExtensionsGuestViewMsg_MimeHandlerViewGuestOnLoadCompleted
,
172 OnMimeHandlerViewGuestOnLoadCompleted
)
173 IPC_MESSAGE_HANDLER(GuestViewMsg_GuestAttached
, OnGuestAttached
)
174 IPC_MESSAGE_UNHANDLED(handled
= false)
175 IPC_END_MESSAGE_MAP()
179 void MimeHandlerViewContainer::DidResizeElement(const gfx::Size
& old_size
,
180 const gfx::Size
& new_size
) {
181 element_size_
= new_size
;
182 render_frame()->Send(new ExtensionsGuestViewHostMsg_ResizeGuest(
183 render_frame()->GetRoutingID(), element_instance_id(), new_size
));
186 v8::Local
<v8::Object
> MimeHandlerViewContainer::V8ScriptableObject(
187 v8::Isolate
* isolate
) {
188 if (scriptable_object_
.IsEmpty()) {
189 v8::Local
<v8::Object
> object
=
190 ScriptableObject::Create(isolate
, weak_factory_
.GetWeakPtr());
191 scriptable_object_
.Reset(isolate
, object
);
193 return v8::Local
<v8::Object
>::New(isolate
, scriptable_object_
);
196 void MimeHandlerViewContainer::didReceiveData(blink::WebURLLoader
* /* unused */,
200 view_id_
+= std::string(data
, data_length
);
203 void MimeHandlerViewContainer::didFinishLoading(
204 blink::WebURLLoader
* /* unused */,
206 int64_t /* unused */) {
207 DCHECK(is_embedded_
);
208 CreateMimeHandlerViewGuest();
211 void MimeHandlerViewContainer::PostMessage(v8::Isolate
* isolate
,
212 v8::Handle
<v8::Value
> message
) {
213 if (!guest_loaded_
) {
214 linked_ptr
<v8::Global
<v8::Value
>> global(
215 new v8::Global
<v8::Value
>(isolate
, message
));
216 pending_messages_
.push_back(global
);
220 content::RenderView
* guest_proxy_render_view
=
221 content::RenderView::FromRoutingID(guest_proxy_routing_id_
);
222 if (!guest_proxy_render_view
)
224 blink::WebFrame
* guest_proxy_frame
=
225 guest_proxy_render_view
->GetWebView()->mainFrame();
226 if (!guest_proxy_frame
)
229 v8::Context::Scope
context_scope(
230 render_frame()->GetWebFrame()->mainWorldScriptContext());
231 v8::Local
<v8::Object
> guest_proxy_window
=
232 guest_proxy_frame
->mainWorldScriptContext()->Global();
233 gin::Dictionary
window_object(isolate
, guest_proxy_window
);
234 v8::Handle
<v8::Function
> post_message
;
235 if (!window_object
.Get(std::string(kPostMessageName
), &post_message
))
238 v8::Handle
<v8::Value
> args
[] = {
240 // Post the message to any domain inside the browser plugin. The embedder
241 // should already know what is embedded.
242 gin::StringToV8(isolate
, "*")
244 guest_proxy_frame
->callFunctionEvenIfScriptDisabled(
245 post_message
.As
<v8::Function
>(),
251 void MimeHandlerViewContainer::PostMessageFromValue(
252 const base::Value
& message
) {
253 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
257 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
258 v8::HandleScope
handle_scope(isolate
);
259 v8::Context::Scope
context_scope(frame
->mainWorldScriptContext());
260 scoped_ptr
<content::V8ValueConverter
> converter(
261 content::V8ValueConverter::create());
263 converter
->ToV8Value(&message
, frame
->mainWorldScriptContext()));
266 void MimeHandlerViewContainer::OnCreateMimeHandlerViewGuestACK(
267 int element_instance_id
) {
268 DCHECK_NE(this->element_instance_id(), guestview::kInstanceIDNone
);
269 DCHECK_EQ(this->element_instance_id(), element_instance_id
);
274 render_frame()->AttachGuest(element_instance_id
);
277 void MimeHandlerViewContainer::OnGuestAttached(int /* unused */,
278 int guest_proxy_routing_id
) {
279 // Save the RenderView routing ID of the guest here so it can be used to route
280 // PostMessage calls.
281 guest_proxy_routing_id_
= guest_proxy_routing_id
;
284 void MimeHandlerViewContainer::OnMimeHandlerViewGuestOnLoadCompleted(
289 guest_loaded_
= true;
290 if (pending_messages_
.empty())
293 // Now that the guest has loaded, flush any unsent messages.
294 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
298 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
299 v8::HandleScope
handle_scope(isolate
);
300 v8::Context::Scope
context_scope(frame
->mainWorldScriptContext());
301 for (const auto& pending_message
: pending_messages_
)
302 PostMessage(isolate
, v8::Local
<v8::Value
>::New(isolate
, *pending_message
));
304 pending_messages_
.clear();
307 void MimeHandlerViewContainer::CreateMimeHandlerViewGuest() {
308 // The loader has completed loading |view_id_| so we can dispose it.
311 DCHECK_NE(element_instance_id(), guestview::kInstanceIDNone
);
316 render_frame()->Send(
317 new ExtensionsGuestViewHostMsg_CreateMimeHandlerViewGuest(
318 render_frame()->GetRoutingID(), view_id_
, element_instance_id(),
322 } // namespace extensions