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