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/guest_view_container.h"
7 #include "content/public/renderer/browser_plugin_delegate.h"
8 #include "content/public/renderer/render_frame.h"
9 #include "content/public/renderer/render_view.h"
10 #include "extensions/common/extension_messages.h"
11 #include "extensions/common/guest_view/guest_view_constants.h"
12 #include "third_party/WebKit/public/web/WebLocalFrame.h"
13 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
14 #include "third_party/WebKit/public/web/WebView.h"
17 typedef std::pair
<int, int> GuestViewID
;
18 typedef std::map
<GuestViewID
, extensions::GuestViewContainer
*>
19 GuestViewContainerMap
;
20 static base::LazyInstance
<GuestViewContainerMap
> g_guest_view_container_map
=
21 LAZY_INSTANCE_INITIALIZER
;
24 namespace extensions
{
26 GuestViewContainer::GuestViewContainer(
27 content::RenderFrame
* render_frame
,
28 const std::string
& mime_type
)
29 : content::BrowserPluginDelegate(render_frame
, mime_type
),
30 content::RenderFrameObserver(render_frame
),
31 mime_type_(mime_type
),
32 element_instance_id_(guestview::kInstanceIDNone
),
33 render_view_routing_id_(render_frame
->GetRenderView()->GetRoutingID()),
35 attach_pending_(false),
39 GuestViewContainer::~GuestViewContainer() {
40 if (element_instance_id_
!= guestview::kInstanceIDNone
) {
41 g_guest_view_container_map
.Get().erase(
42 GuestViewID(render_view_routing_id_
, element_instance_id_
));
46 GuestViewContainer
* GuestViewContainer::FromID(int render_view_routing_id
,
47 int element_instance_id
) {
48 GuestViewContainerMap
* guest_view_containers
=
49 g_guest_view_container_map
.Pointer();
50 GuestViewContainerMap::iterator it
= guest_view_containers
->find(
51 GuestViewID(render_view_routing_id
, element_instance_id
));
52 return it
== guest_view_containers
->end() ? NULL
: it
->second
;
56 void GuestViewContainer::AttachGuest(int element_instance_id
,
57 int guest_instance_id
,
58 scoped_ptr
<base::DictionaryValue
> params
,
59 v8::Handle
<v8::Function
> callback
,
60 v8::Isolate
* isolate
) {
61 // GuestViewContainer supports reattachment (i.e. attached_ == true) but not
62 // while a current attach process is pending.
66 // Step 1, send the attach params to chrome/.
67 render_frame()->Send(new ExtensionHostMsg_AttachGuest(render_view_routing_id_
,
72 // Step 2, attach plugin through content/.
73 render_frame()->AttachGuest(element_instance_id
);
75 callback_
.reset(callback
);
77 attach_pending_
= true;
80 void GuestViewContainer::SetElementInstanceID(int element_instance_id
) {
81 GuestViewID
guest_view_id(render_view_routing_id_
, element_instance_id
);
82 DCHECK_EQ(element_instance_id_
, guestview::kInstanceIDNone
);
83 DCHECK(g_guest_view_container_map
.Get().find(guest_view_id
) ==
84 g_guest_view_container_map
.Get().end());
85 element_instance_id_
= element_instance_id
;
86 g_guest_view_container_map
.Get().insert(std::make_pair(guest_view_id
, this));
89 void GuestViewContainer::DidFinishLoading() {
90 if (mime_type_
.empty())
93 DCHECK_NE(element_instance_id_
, guestview::kInstanceIDNone
);
94 render_frame()->Send(new ExtensionHostMsg_CreateMimeHandlerViewGuest(
95 routing_id(), html_string_
, mime_type_
, element_instance_id_
));
98 void GuestViewContainer::DidReceiveData(const char* data
, int data_length
) {
99 std::string
value(data
, data_length
);
100 html_string_
+= value
;
103 void GuestViewContainer::OnDestruct() {
104 // GuestViewContainer's lifetime is managed by BrowserPlugin so don't let
105 // RenderFrameObserver self-destruct here.
108 bool GuestViewContainer::OnMessageReceived(const IPC::Message
& message
) {
109 if (!ShouldHandleMessage(message
))
112 DCHECK_NE(element_instance_id_
, guestview::kInstanceIDNone
);
113 int element_instance_id
= guestview::kInstanceIDNone
;
114 PickleIterator
iter(message
);
115 bool success
= iter
.ReadInt(&element_instance_id
);
117 if (element_instance_id
!= element_instance_id_
)
121 IPC_BEGIN_MESSAGE_MAP(GuestViewContainer
, message
)
122 IPC_MESSAGE_HANDLER(ExtensionMsg_CreateMimeHandlerViewGuestACK
,
123 OnCreateMimeHandlerViewGuestACK
)
124 IPC_MESSAGE_HANDLER(ExtensionMsg_GuestAttached
, OnGuestAttached
)
125 IPC_MESSAGE_UNHANDLED(handled
= false)
126 IPC_END_MESSAGE_MAP()
130 void GuestViewContainer::OnCreateMimeHandlerViewGuestACK(
131 int element_instance_id
) {
132 DCHECK_NE(element_instance_id_
, guestview::kInstanceIDNone
);
133 DCHECK_EQ(element_instance_id_
, element_instance_id
);
134 DCHECK(!mime_type_
.empty());
135 render_frame()->AttachGuest(element_instance_id
);
138 void GuestViewContainer::OnGuestAttached(int element_instance_id
,
139 int guest_routing_id
) {
141 attach_pending_
= false;
143 // If we don't have a callback then there's nothing more to do.
144 if (callback_
.IsEmpty())
147 content::RenderView
* guest_proxy_render_view
=
148 content::RenderView::FromRoutingID(guest_routing_id
);
149 // TODO(fsamuel): Should we be reporting an error to JavaScript or DCHECKing?
150 if (!guest_proxy_render_view
)
153 v8::HandleScope
handle_scope(isolate_
);
154 v8::Handle
<v8::Function
> callback
= callback_
.NewHandle(isolate_
);
155 v8::Handle
<v8::Context
> context
= callback
->CreationContext();
156 if (context
.IsEmpty())
159 blink::WebFrame
* frame
= guest_proxy_render_view
->GetWebView()->mainFrame();
160 v8::Local
<v8::Value
> window
= frame
->mainWorldScriptContext()->Global();
163 v8::Handle
<v8::Value
> argv
[argc
] = { window
};
165 v8::Context::Scope
context_scope(context
);
166 blink::WebScopedMicrotaskSuppression suppression
;
168 // Call the AttachGuest API's callback with the guest proxy as the first
170 callback
->Call(context
->Global(), argc
, argv
);
175 bool GuestViewContainer::ShouldHandleMessage(const IPC::Message
& message
) {
176 switch (message
.type()) {
177 case ExtensionMsg_CreateMimeHandlerViewGuestACK::ID
:
178 case ExtensionMsg_GuestAttached::ID
:
186 } // namespace extensions