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.
4
5 #include "extensions/browser/guest_view/app_view/app_view_guest.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/lazy_instance.h"
12 #include "components/guest_view/browser/guest_view_manager.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "extensions/browser/api/app_runtime/app_runtime_api.h"
15 #include "extensions/browser/api/extensions_api_client.h"
16 #include "extensions/browser/app_window/app_delegate.h"
17 #include "extensions/browser/bad_message.h"
18 #include "extensions/browser/event_router.h"
19 #include "extensions/browser/extension_host.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/browser/guest_view/app_view/app_view_constants.h"
22 #include "extensions/browser/lazy_context_id.h"
23 #include "extensions/browser/lazy_context_task_queue.h"
24 #include "extensions/browser/process_manager.h"
25 #include "extensions/browser/view_type_utils.h"
26 #include "extensions/common/api/app_runtime.h"
27 #include "extensions/common/extension_messages.h"
28 #include "extensions/strings/grit/extensions_strings.h"
29 #include "ipc/ipc_message_macros.h"
30
31 namespace app_runtime = extensions::api::app_runtime;
32
33 using content::RenderFrameHost;
34 using content::WebContents;
35 using extensions::ExtensionHost;
36 using guest_view::GuestViewBase;
37
38 namespace extensions {
39
40 namespace {
41
42 struct ResponseInfo {
43 scoped_refptr<const Extension> guest_extension;
44 base::WeakPtr<AppViewGuest> app_view_guest;
45 GuestViewBase::WebContentsCreatedCallback callback;
46
ResponseInfoextensions::__anonef84e7480111::ResponseInfo47 ResponseInfo(const Extension* guest_extension,
48 const base::WeakPtr<AppViewGuest>& app_view_guest,
49 GuestViewBase::WebContentsCreatedCallback callback)
50 : guest_extension(guest_extension),
51 app_view_guest(app_view_guest),
52 callback(std::move(callback)) {}
53
~ResponseInfoextensions::__anonef84e7480111::ResponseInfo54 ~ResponseInfo() {}
55 };
56
57 using PendingResponseMap = std::map<int, std::unique_ptr<ResponseInfo>>;
58 base::LazyInstance<PendingResponseMap>::DestructorAtExit
59 g_pending_response_map = LAZY_INSTANCE_INITIALIZER;
60
61 } // namespace
62
63 // static.
64 const char AppViewGuest::Type[] = "appview";
65
66 // static.
CompletePendingRequest(content::BrowserContext * browser_context,const GURL & url,int guest_instance_id,const std::string & guest_extension_id,content::RenderProcessHost * guest_render_process_host)67 bool AppViewGuest::CompletePendingRequest(
68 content::BrowserContext* browser_context,
69 const GURL& url,
70 int guest_instance_id,
71 const std::string& guest_extension_id,
72 content::RenderProcessHost* guest_render_process_host) {
73 PendingResponseMap* response_map = g_pending_response_map.Pointer();
74 auto it = response_map->find(guest_instance_id);
75 // Kill the requesting process if it is not the real guest.
76 if (it == response_map->end()) {
77 // The requester used an invalid |guest_instance_id|.
78 bad_message::ReceivedBadMessage(guest_render_process_host,
79 bad_message::AVG_BAD_INST_ID);
80 return false;
81 }
82
83 ResponseInfo* response_info = it->second.get();
84 if (!response_info->app_view_guest ||
85 (response_info->guest_extension->id() != guest_extension_id)) {
86 // The app is trying to communicate with an <appview> not assigned to it, or
87 // the <appview> is already dead "nullptr".
88 bad_message::BadMessageReason reason = !response_info->app_view_guest
89 ? bad_message::AVG_NULL_AVG
90 : bad_message::AVG_BAD_EXT_ID;
91 bad_message::ReceivedBadMessage(guest_render_process_host, reason);
92 return false;
93 }
94
95 response_info->app_view_guest->CompleteCreateWebContents(
96 url, response_info->guest_extension.get(),
97 std::move(response_info->callback));
98
99 response_map->erase(guest_instance_id);
100 return true;
101 }
102
103 // static
Create(WebContents * owner_web_contents)104 GuestViewBase* AppViewGuest::Create(WebContents* owner_web_contents) {
105 return new AppViewGuest(owner_web_contents);
106 }
107
AppViewGuest(WebContents * owner_web_contents)108 AppViewGuest::AppViewGuest(WebContents* owner_web_contents)
109 : GuestView<AppViewGuest>(owner_web_contents),
110 app_view_guest_delegate_(
111 ExtensionsAPIClient::Get()->CreateAppViewGuestDelegate()) {
112 if (app_view_guest_delegate_)
113 app_delegate_.reset(app_view_guest_delegate_->CreateAppDelegate());
114 }
115
~AppViewGuest()116 AppViewGuest::~AppViewGuest() {
117 }
118
HandleContextMenu(content::RenderFrameHost * render_frame_host,const content::ContextMenuParams & params)119 bool AppViewGuest::HandleContextMenu(
120 content::RenderFrameHost* render_frame_host,
121 const content::ContextMenuParams& params) {
122 if (app_view_guest_delegate_) {
123 return app_view_guest_delegate_->HandleContextMenu(web_contents(), params);
124 }
125 return false;
126 }
127
RequestMediaAccessPermission(WebContents * web_contents,const content::MediaStreamRequest & request,content::MediaResponseCallback callback)128 void AppViewGuest::RequestMediaAccessPermission(
129 WebContents* web_contents,
130 const content::MediaStreamRequest& request,
131 content::MediaResponseCallback callback) {
132 if (!app_delegate_) {
133 WebContentsDelegate::RequestMediaAccessPermission(web_contents, request,
134 std::move(callback));
135 return;
136 }
137 const ExtensionSet& enabled_extensions =
138 ExtensionRegistry::Get(browser_context())->enabled_extensions();
139 const Extension* guest_extension =
140 enabled_extensions.GetByID(guest_extension_id_);
141
142 app_delegate_->RequestMediaAccessPermission(
143 web_contents, request, std::move(callback), guest_extension);
144 }
145
CheckMediaAccessPermission(content::RenderFrameHost * render_frame_host,const GURL & security_origin,blink::mojom::MediaStreamType type)146 bool AppViewGuest::CheckMediaAccessPermission(
147 content::RenderFrameHost* render_frame_host,
148 const GURL& security_origin,
149 blink::mojom::MediaStreamType type) {
150 if (!app_delegate_) {
151 return WebContentsDelegate::CheckMediaAccessPermission(
152 render_frame_host, security_origin, type);
153 }
154 const ExtensionSet& enabled_extensions =
155 ExtensionRegistry::Get(browser_context())->enabled_extensions();
156 const Extension* guest_extension =
157 enabled_extensions.GetByID(guest_extension_id_);
158
159 return app_delegate_->CheckMediaAccessPermission(
160 render_frame_host, security_origin, type, guest_extension);
161 }
162
CreateWebContents(const base::DictionaryValue & create_params,WebContentsCreatedCallback callback)163 void AppViewGuest::CreateWebContents(const base::DictionaryValue& create_params,
164 WebContentsCreatedCallback callback) {
165 std::string app_id;
166 if (!create_params.GetString(appview::kAppID, &app_id)) {
167 std::move(callback).Run(nullptr);
168 return;
169 }
170 // Verifying that the appId is not the same as the host application.
171 if (owner_host() == app_id) {
172 std::move(callback).Run(nullptr);
173 return;
174 }
175 const base::DictionaryValue* data = nullptr;
176 if (!create_params.GetDictionary(appview::kData, &data)) {
177 std::move(callback).Run(nullptr);
178 return;
179 }
180
181 const ExtensionSet& enabled_extensions =
182 ExtensionRegistry::Get(browser_context())->enabled_extensions();
183 const Extension* guest_extension = enabled_extensions.GetByID(app_id);
184 const Extension* embedder_extension =
185 enabled_extensions.GetByID(GetOwnerSiteURL().host());
186
187 if (!guest_extension || !guest_extension->is_platform_app() ||
188 !embedder_extension || !embedder_extension->is_platform_app()) {
189 std::move(callback).Run(nullptr);
190 return;
191 }
192
193 const LazyContextId context_id(browser_context(), guest_extension->id());
194 LazyContextTaskQueue* queue = context_id.GetTaskQueue();
195 if (queue->ShouldEnqueueTask(browser_context(), guest_extension)) {
196 queue->AddPendingTask(
197 context_id,
198 base::BindOnce(&AppViewGuest::LaunchAppAndFireEvent,
199 weak_ptr_factory_.GetWeakPtr(), data->CreateDeepCopy(),
200 std::move(callback)));
201 return;
202 }
203
204 ProcessManager* process_manager = ProcessManager::Get(browser_context());
205 ExtensionHost* host =
206 process_manager->GetBackgroundHostForExtension(guest_extension->id());
207 DCHECK(host);
208 LaunchAppAndFireEvent(
209 data->CreateDeepCopy(), std::move(callback),
210 std::make_unique<LazyContextTaskQueue::ContextInfo>(host));
211 }
212
DidInitialize(const base::DictionaryValue & create_params)213 void AppViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
214 ExtensionsAPIClient::Get()->AttachWebContentsHelpers(web_contents());
215
216 if (!url_.is_valid())
217 return;
218
219 web_contents()->GetController().LoadURL(
220 url_, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
221 }
222
GetAPINamespace() const223 const char* AppViewGuest::GetAPINamespace() const {
224 return appview::kEmbedderAPINamespace;
225 }
226
GetTaskPrefix() const227 int AppViewGuest::GetTaskPrefix() const {
228 return IDS_EXTENSION_TASK_MANAGER_APPVIEW_TAG_PREFIX;
229 }
230
CompleteCreateWebContents(const GURL & url,const Extension * guest_extension,WebContentsCreatedCallback callback)231 void AppViewGuest::CompleteCreateWebContents(
232 const GURL& url,
233 const Extension* guest_extension,
234 WebContentsCreatedCallback callback) {
235 if (!url.is_valid()) {
236 std::move(callback).Run(nullptr);
237 return;
238 }
239 url_ = url;
240 guest_extension_id_ = guest_extension->id();
241
242 WebContents::CreateParams params(
243 browser_context(),
244 content::SiteInstance::CreateForURL(browser_context(),
245 guest_extension->url()));
246 params.guest_delegate = this;
247 // TODO(erikchen): Fix ownership semantics for guest views.
248 // https://crbug.com/832879.
249 std::move(callback).Run(WebContents::Create(params).release());
250 }
251
LaunchAppAndFireEvent(std::unique_ptr<base::DictionaryValue> data,WebContentsCreatedCallback callback,std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info)252 void AppViewGuest::LaunchAppAndFireEvent(
253 std::unique_ptr<base::DictionaryValue> data,
254 WebContentsCreatedCallback callback,
255 std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info) {
256 bool has_event_listener = EventRouter::Get(browser_context())
257 ->ExtensionHasEventListener(
258 context_info->extension_id,
259 app_runtime::OnEmbedRequested::kEventName);
260 if (!has_event_listener) {
261 std::move(callback).Run(nullptr);
262 return;
263 }
264
265 const Extension* const extension =
266 extensions::ExtensionRegistry::Get(context_info->browser_context)
267 ->enabled_extensions()
268 .GetByID(context_info->extension_id);
269
270 g_pending_response_map.Get().insert(std::make_pair(
271 guest_instance_id(),
272 std::make_unique<ResponseInfo>(extension, weak_ptr_factory_.GetWeakPtr(),
273 std::move(callback))));
274
275 std::unique_ptr<base::DictionaryValue> embed_request(
276 new base::DictionaryValue());
277 embed_request->SetInteger(appview::kGuestInstanceID, guest_instance_id());
278 embed_request->SetString(appview::kEmbedderID, owner_host());
279 embed_request->Set(appview::kData, std::move(data));
280 AppRuntimeEventRouter::DispatchOnEmbedRequestedEvent(
281 browser_context(), std::move(embed_request), extension);
282 }
283
SetAppDelegateForTest(AppDelegate * delegate)284 void AppViewGuest::SetAppDelegateForTest(AppDelegate* delegate) {
285 app_delegate_.reset(delegate);
286 }
287
GetAllRegisteredInstanceIdsForTesting()288 std::vector<int> AppViewGuest::GetAllRegisteredInstanceIdsForTesting() {
289 std::vector<int> instances;
290 for (const auto& key_value : g_pending_response_map.Get()) {
291 instances.push_back(key_value.first);
292 }
293 return instances;
294 }
295
296 } // namespace extensions
297