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::__anon9d0efa230111::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::__anon9d0efa230111::ResponseInfo54   ~ResponseInfo() {}
55 };
56 
57 using PendingResponseMap = std::map<int, std::unique_ptr<ResponseInfo>>;
58 static base::LazyInstance<PendingResponseMap>::DestructorAtExit
59     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 = 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   pending_response_map.Get().insert(std::make_pair(
194       guest_instance_id(), std::make_unique<ResponseInfo>(
195                                guest_extension, weak_ptr_factory_.GetWeakPtr(),
196                                std::move(callback))));
197 
198   const LazyContextId context_id(browser_context(), guest_extension->id());
199   LazyContextTaskQueue* queue = context_id.GetTaskQueue();
200   if (queue->ShouldEnqueueTask(browser_context(), guest_extension)) {
201     queue->AddPendingTask(
202         context_id,
203         base::BindOnce(&AppViewGuest::LaunchAppAndFireEvent,
204                        weak_ptr_factory_.GetWeakPtr(), data->CreateDeepCopy(),
205                        std::move(callback)));
206     return;
207   }
208 
209   ProcessManager* process_manager = ProcessManager::Get(browser_context());
210   ExtensionHost* host =
211       process_manager->GetBackgroundHostForExtension(guest_extension->id());
212   DCHECK(host);
213   LaunchAppAndFireEvent(
214       data->CreateDeepCopy(), std::move(callback),
215       std::make_unique<LazyContextTaskQueue::ContextInfo>(host));
216 }
217 
DidInitialize(const base::DictionaryValue & create_params)218 void AppViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
219   ExtensionsAPIClient::Get()->AttachWebContentsHelpers(web_contents());
220 
221   if (!url_.is_valid())
222     return;
223 
224   web_contents()->GetController().LoadURL(
225       url_, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
226 }
227 
GetAPINamespace() const228 const char* AppViewGuest::GetAPINamespace() const {
229   return appview::kEmbedderAPINamespace;
230 }
231 
GetTaskPrefix() const232 int AppViewGuest::GetTaskPrefix() const {
233   return IDS_EXTENSION_TASK_MANAGER_APPVIEW_TAG_PREFIX;
234 }
235 
CompleteCreateWebContents(const GURL & url,const Extension * guest_extension,WebContentsCreatedCallback callback)236 void AppViewGuest::CompleteCreateWebContents(
237     const GURL& url,
238     const Extension* guest_extension,
239     WebContentsCreatedCallback callback) {
240   if (!url.is_valid()) {
241     std::move(callback).Run(nullptr);
242     return;
243   }
244   url_ = url;
245   guest_extension_id_ = guest_extension->id();
246 
247   WebContents::CreateParams params(
248       browser_context(),
249       content::SiteInstance::CreateForURL(browser_context(),
250                                           guest_extension->url()));
251   params.guest_delegate = this;
252   // TODO(erikchen): Fix ownership semantics for guest views.
253   // https://crbug.com/832879.
254   std::move(callback).Run(WebContents::Create(params).release());
255 }
256 
LaunchAppAndFireEvent(std::unique_ptr<base::DictionaryValue> data,WebContentsCreatedCallback callback,std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info)257 void AppViewGuest::LaunchAppAndFireEvent(
258     std::unique_ptr<base::DictionaryValue> data,
259     WebContentsCreatedCallback callback,
260     std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info) {
261   bool has_event_listener = EventRouter::Get(browser_context())
262                                 ->ExtensionHasEventListener(
263                                     context_info->extension_id,
264                                     app_runtime::OnEmbedRequested::kEventName);
265   if (!has_event_listener) {
266     std::move(callback).Run(nullptr);
267     return;
268   }
269 
270   std::unique_ptr<base::DictionaryValue> embed_request(
271       new base::DictionaryValue());
272   embed_request->SetInteger(appview::kGuestInstanceID, guest_instance_id());
273   embed_request->SetString(appview::kEmbedderID, owner_host());
274   embed_request->Set(appview::kData, std::move(data));
275   const Extension* const extension =
276       extensions::ExtensionRegistry::Get(context_info->browser_context)
277           ->enabled_extensions()
278           .GetByID(context_info->extension_id);
279   AppRuntimeEventRouter::DispatchOnEmbedRequestedEvent(
280       browser_context(), std::move(embed_request), extension);
281 }
282 
SetAppDelegateForTest(AppDelegate * delegate)283 void AppViewGuest::SetAppDelegateForTest(AppDelegate* delegate) {
284   app_delegate_.reset(delegate);
285 }
286 
GetAllRegisteredInstanceIdsForTesting()287 std::vector<int> AppViewGuest::GetAllRegisteredInstanceIdsForTesting() {
288   std::vector<int> instances;
289   for (const auto& key_value : pending_response_map.Get()) {
290     instances.push_back(key_value.first);
291   }
292   return instances;
293 }
294 
295 }  // namespace extensions
296