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 "base/command_line.h" 8#include "content/public/browser/render_view_host.h" 9#include "content/public/common/renderer_preferences.h" 10#include "extensions/browser/api/app_runtime/app_runtime_api.h" 11#include "extensions/browser/api/extensions_api_client.h" 12#include "extensions/browser/event_router.h" 13#include "extensions/browser/extension_host.h" 14#include "extensions/browser/extension_registry.h" 15#include "extensions/browser/extension_system.h" 16#include "extensions/browser/guest_view/app_view/app_view_constants.h" 17#include "extensions/browser/guest_view/guest_view_manager.h" 18#include "extensions/browser/lazy_background_task_queue.h" 19#include "extensions/browser/process_manager.h" 20#include "extensions/browser/view_type_utils.h" 21#include "extensions/common/api/app_runtime.h" 22#include "extensions/common/extension_messages.h" 23#include "extensions/common/switches.h" 24#include "extensions/strings/grit/extensions_strings.h" 25#include "ipc/ipc_message_macros.h" 26 27namespace app_runtime = extensions::core_api::app_runtime; 28 29using content::RenderFrameHost; 30using content::WebContents; 31using extensions::ExtensionHost; 32 33namespace extensions { 34 35namespace { 36 37struct ResponseInfo { 38 scoped_refptr<const Extension> guest_extension; 39 base::WeakPtr<AppViewGuest> app_view_guest; 40 GuestViewBase::WebContentsCreatedCallback callback; 41 42 ResponseInfo(const Extension* guest_extension, 43 const base::WeakPtr<AppViewGuest>& app_view_guest, 44 const GuestViewBase::WebContentsCreatedCallback& callback) 45 : guest_extension(guest_extension), 46 app_view_guest(app_view_guest), 47 callback(callback) {} 48 49 ~ResponseInfo() {} 50}; 51 52typedef std::map<int, linked_ptr<ResponseInfo> > PendingResponseMap; 53static base::LazyInstance<PendingResponseMap> pending_response_map = 54 LAZY_INSTANCE_INITIALIZER; 55 56} // namespace 57 58// static. 59const char AppViewGuest::Type[] = "appview"; 60 61// static. 62bool AppViewGuest::CompletePendingRequest( 63 content::BrowserContext* browser_context, 64 const GURL& url, 65 int guest_instance_id, 66 const std::string& guest_extension_id) { 67 PendingResponseMap* response_map = pending_response_map.Pointer(); 68 PendingResponseMap::iterator it = response_map->find(guest_instance_id); 69 if (it == response_map->end()) { 70 // TODO(fsamuel): An app is sending invalid responses. We should probably 71 // kill it. 72 return false; 73 } 74 75 linked_ptr<ResponseInfo> response_info = it->second; 76 if (!response_info->app_view_guest || 77 (response_info->guest_extension->id() != guest_extension_id)) { 78 // TODO(fsamuel): An app is trying to respond to an <appview> that didn't 79 // initiate communication with it. We should kill the app here. 80 return false; 81 } 82 83 response_info->app_view_guest->CompleteCreateWebContents( 84 url, response_info->guest_extension.get(), response_info->callback); 85 86 response_map->erase(guest_instance_id); 87 return true; 88} 89 90// static 91GuestViewBase* AppViewGuest::Create(content::BrowserContext* browser_context, 92 int guest_instance_id) { 93 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( 94 extensions::switches::kEnableAppView)) { 95 return NULL; 96 } 97 return new AppViewGuest(browser_context, guest_instance_id); 98} 99 100AppViewGuest::AppViewGuest(content::BrowserContext* browser_context, 101 int guest_instance_id) 102 : GuestView<AppViewGuest>(browser_context, guest_instance_id), 103 app_view_guest_delegate_( 104 ExtensionsAPIClient::Get()->CreateAppViewGuestDelegate()), 105 weak_ptr_factory_(this) { 106} 107 108AppViewGuest::~AppViewGuest() { 109} 110 111WindowController* AppViewGuest::GetExtensionWindowController() const { 112 return NULL; 113} 114 115content::WebContents* AppViewGuest::GetAssociatedWebContents() const { 116 return web_contents(); 117} 118 119bool AppViewGuest::OnMessageReceived(const IPC::Message& message) { 120 bool handled = true; 121 IPC_BEGIN_MESSAGE_MAP(AppViewGuest, message) 122 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) 123 IPC_MESSAGE_UNHANDLED(handled = false) 124 IPC_END_MESSAGE_MAP() 125 return handled; 126} 127 128bool AppViewGuest::HandleContextMenu(const content::ContextMenuParams& params) { 129 if (app_view_guest_delegate_) { 130 return app_view_guest_delegate_->HandleContextMenu(web_contents(), params); 131 } 132 return false; 133} 134 135const char* AppViewGuest::GetAPINamespace() const { 136 return appview::kEmbedderAPINamespace; 137} 138 139int AppViewGuest::GetTaskPrefix() const { 140 return IDS_EXTENSION_TASK_MANAGER_APPVIEW_TAG_PREFIX; 141} 142 143void AppViewGuest::CreateWebContents( 144 const std::string& embedder_extension_id, 145 int embedder_render_process_id, 146 const GURL& embedder_site_url, 147 const base::DictionaryValue& create_params, 148 const WebContentsCreatedCallback& callback) { 149 std::string app_id; 150 if (!create_params.GetString(appview::kAppID, &app_id)) { 151 callback.Run(NULL); 152 return; 153 } 154 155 const base::DictionaryValue* data = NULL; 156 if (!create_params.GetDictionary(appview::kData, &data)) { 157 callback.Run(NULL); 158 return; 159 } 160 161 const ExtensionSet& enabled_extensions = 162 ExtensionRegistry::Get(browser_context())->enabled_extensions(); 163 const Extension* guest_extension = enabled_extensions.GetByID(app_id); 164 const Extension* embedder_extension = 165 enabled_extensions.GetByID(embedder_extension_id); 166 167 if (!guest_extension || !guest_extension->is_platform_app() || 168 !embedder_extension | !embedder_extension->is_platform_app()) { 169 callback.Run(NULL); 170 return; 171 } 172 173 pending_response_map.Get().insert( 174 std::make_pair(guest_instance_id(), 175 make_linked_ptr(new ResponseInfo( 176 guest_extension, 177 weak_ptr_factory_.GetWeakPtr(), 178 callback)))); 179 180 LazyBackgroundTaskQueue* queue = 181 ExtensionSystem::Get(browser_context())->lazy_background_task_queue(); 182 if (queue->ShouldEnqueueTask(browser_context(), guest_extension)) { 183 queue->AddPendingTask(browser_context(), 184 guest_extension->id(), 185 base::Bind( 186 &AppViewGuest::LaunchAppAndFireEvent, 187 weak_ptr_factory_.GetWeakPtr(), 188 base::Passed(make_scoped_ptr(data->DeepCopy())), 189 callback)); 190 return; 191 } 192 193 ProcessManager* process_manager = 194 ExtensionSystem::Get(browser_context())->process_manager(); 195 ExtensionHost* host = 196 process_manager->GetBackgroundHostForExtension(guest_extension->id()); 197 DCHECK(host); 198 LaunchAppAndFireEvent(make_scoped_ptr(data->DeepCopy()), callback, host); 199} 200 201void AppViewGuest::DidAttachToEmbedder() { 202 // This is called after the guest process has been attached to a host 203 // element. This means that the host element knows how to route input 204 // events to the guest, and the guest knows how to get frames to the 205 // embedder. 206 web_contents()->GetController().LoadURL( 207 url_, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); 208} 209 210void AppViewGuest::DidInitialize() { 211 extension_function_dispatcher_.reset( 212 new ExtensionFunctionDispatcher(browser_context(), this)); 213} 214 215void AppViewGuest::OnRequest(const ExtensionHostMsg_Request_Params& params) { 216 extension_function_dispatcher_->Dispatch(params, 217 web_contents()->GetRenderViewHost()); 218} 219 220void AppViewGuest::CompleteCreateWebContents( 221 const GURL& url, 222 const Extension* guest_extension, 223 const WebContentsCreatedCallback& callback) { 224 if (!url.is_valid()) { 225 callback.Run(NULL); 226 return; 227 } 228 url_ = url; 229 guest_extension_id_ = guest_extension->id(); 230 231 WebContents::CreateParams params( 232 browser_context(), 233 content::SiteInstance::CreateForURL(browser_context(), 234 guest_extension->url())); 235 params.guest_delegate = this; 236 callback.Run(WebContents::Create(params)); 237} 238 239void AppViewGuest::LaunchAppAndFireEvent( 240 scoped_ptr<base::DictionaryValue> data, 241 const WebContentsCreatedCallback& callback, 242 ExtensionHost* extension_host) { 243 ExtensionSystem* system = ExtensionSystem::Get(browser_context()); 244 bool has_event_listener = system->event_router()->ExtensionHasEventListener( 245 extension_host->extension()->id(), 246 app_runtime::OnEmbedRequested::kEventName); 247 if (!has_event_listener) { 248 callback.Run(NULL); 249 return; 250 } 251 252 scoped_ptr<base::DictionaryValue> embed_request(new base::DictionaryValue()); 253 embed_request->SetInteger(appview::kGuestInstanceID, guest_instance_id()); 254 embed_request->SetString(appview::kEmbedderID, embedder_extension_id()); 255 embed_request->Set(appview::kData, data.release()); 256 AppRuntimeEventRouter::DispatchOnEmbedRequestedEvent( 257 browser_context(), embed_request.Pass(), extension_host->extension()); 258} 259 260} // namespace extensions 261