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