1// Copyright 2013 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 "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
6
7#include "base/values.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
10#include "chrome/browser/extensions/api/tabs/tabs_windows_api.h"
11#include "chrome/browser/extensions/api/tabs/windows_event_router.h"
12#include "chrome/browser/extensions/extension_tab_util.h"
13#include "chrome/browser/extensions/window_controller_list.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/sessions/session_tab_helper.h"
16#include "content/public/browser/browser_context.h"
17#include "content/public/browser/site_instance.h"
18#include "content/public/browser/web_contents.h"
19#include "extensions/browser/app_window/native_app_window.h"
20#include "extensions/common/extension.h"
21#include "extensions/common/extension_messages.h"
22#include "ui/gfx/image/image.h"
23
24using extensions::AppWindow;
25using extensions::NativeAppWindow;
26
27// AshPanelWindowController ----------------------------------------------------
28
29// This class enables an AppWindow instance to be accessed (to a limited
30// extent) via the chrome.windows and chrome.tabs API. This is a temporary
31// bridge to support instantiating AppWindows from v1 apps, specifically
32// for creating Panels in Ash. See crbug.com/160645.
33class AshPanelWindowController : public extensions::WindowController {
34 public:
35  AshPanelWindowController(AppWindow* window, Profile* profile);
36  virtual ~AshPanelWindowController();
37
38  void NativeWindowChanged();
39
40  // Overridden from extensions::WindowController.
41  virtual int GetWindowId() const OVERRIDE;
42  virtual std::string GetWindowTypeText() const OVERRIDE;
43  virtual base::DictionaryValue* CreateWindowValueWithTabs(
44      const extensions::Extension* extension) const OVERRIDE;
45  virtual base::DictionaryValue* CreateTabValue(
46      const extensions::Extension* extension, int tab_index) const OVERRIDE;
47  virtual bool CanClose(Reason* reason) const OVERRIDE;
48  virtual void SetFullscreenMode(bool is_fullscreen,
49                                 const GURL& extension_url) const OVERRIDE;
50  virtual bool IsVisibleToExtension(
51      const extensions::Extension* extension) const OVERRIDE;
52
53 private:
54  AppWindow* app_window_;  // Weak pointer; this is owned by app_window_
55  bool is_active_;
56
57  DISALLOW_COPY_AND_ASSIGN(AshPanelWindowController);
58};
59
60AshPanelWindowController::AshPanelWindowController(AppWindow* app_window,
61                                                   Profile* profile)
62    : extensions::WindowController(app_window->GetBaseWindow(), profile),
63      app_window_(app_window),
64      is_active_(app_window->GetBaseWindow()->IsActive()) {
65  extensions::WindowControllerList::GetInstance()->AddExtensionWindow(this);
66}
67
68AshPanelWindowController::~AshPanelWindowController() {
69  extensions::WindowControllerList::GetInstance()->RemoveExtensionWindow(this);
70}
71
72int AshPanelWindowController::GetWindowId() const {
73  return static_cast<int>(app_window_->session_id().id());
74}
75
76std::string AshPanelWindowController::GetWindowTypeText() const {
77  return extensions::tabs_constants::kWindowTypeValuePanel;
78}
79
80base::DictionaryValue* AshPanelWindowController::CreateWindowValueWithTabs(
81    const extensions::Extension* extension) const {
82  DCHECK(IsVisibleToExtension(extension));
83  base::DictionaryValue* result = CreateWindowValue();
84  base::DictionaryValue* tab_value = CreateTabValue(extension, 0);
85  if (tab_value) {
86    base::ListValue* tab_list = new base::ListValue();
87    tab_list->Append(tab_value);
88    result->Set(extensions::tabs_constants::kTabsKey, tab_list);
89  }
90  return result;
91}
92
93base::DictionaryValue* AshPanelWindowController::CreateTabValue(
94    const extensions::Extension* extension, int tab_index) const {
95  if ((extension && !IsVisibleToExtension(extension)) ||
96      (tab_index > 0)) {
97    return NULL;
98  }
99  content::WebContents* web_contents = app_window_->web_contents();
100  if (!web_contents)
101    return NULL;
102
103  base::DictionaryValue* tab_value = new base::DictionaryValue();
104  tab_value->SetInteger(extensions::tabs_constants::kIdKey,
105                        SessionTabHelper::IdForTab(web_contents));
106  tab_value->SetInteger(extensions::tabs_constants::kIndexKey, 0);
107  const int window_id = GetWindowId();
108  tab_value->SetInteger(extensions::tabs_constants::kWindowIdKey, window_id);
109  tab_value->SetString(
110      extensions::tabs_constants::kUrlKey, web_contents->GetURL().spec());
111  tab_value->SetString(
112      extensions::tabs_constants::kStatusKey,
113      extensions::ExtensionTabUtil::GetTabStatusText(
114          web_contents->IsLoading()));
115  tab_value->SetBoolean(extensions::tabs_constants::kActiveKey,
116                        app_window_->GetBaseWindow()->IsActive());
117  // AppWindow only ever contains one tab, so that tab is always effectively
118  // selcted and highlighted (for purposes of the chrome.tabs API).
119  tab_value->SetInteger(extensions::tabs_constants::kWindowIdKey, window_id);
120  tab_value->SetInteger(extensions::tabs_constants::kIdKey, window_id);
121  tab_value->SetBoolean(extensions::tabs_constants::kSelectedKey, true);
122  tab_value->SetBoolean(extensions::tabs_constants::kHighlightedKey, true);
123  tab_value->SetBoolean(extensions::tabs_constants::kPinnedKey, false);
124  tab_value->SetString(
125      extensions::tabs_constants::kTitleKey, web_contents->GetTitle());
126  tab_value->SetBoolean(
127      extensions::tabs_constants::kIncognitoKey,
128      web_contents->GetBrowserContext()->IsOffTheRecord());
129  return tab_value;
130}
131
132bool AshPanelWindowController::CanClose(Reason* reason) const {
133  return true;
134}
135
136void AshPanelWindowController::SetFullscreenMode(
137    bool is_fullscreen, const GURL& extension_url) const {
138  // Do nothing. Panels cannot be fullscreen.
139}
140
141bool AshPanelWindowController::IsVisibleToExtension(
142    const extensions::Extension* extension) const {
143  return extension->id() == app_window_->extension_id();
144}
145
146void AshPanelWindowController::NativeWindowChanged() {
147  bool active = app_window_->GetBaseWindow()->IsActive();
148  if (active == is_active_)
149    return;
150  is_active_ = active;
151  // Let the extension API know that the active window changed.
152  extensions::TabsWindowsAPI* tabs_windows_api =
153      extensions::TabsWindowsAPI::Get(profile());
154  if (!tabs_windows_api)
155    return;
156  tabs_windows_api->windows_event_router()->OnActiveWindowChanged(
157      active ? this : NULL);
158}
159
160// AshPanelContents -----------------------------------------------------
161
162AshPanelContents::AshPanelContents(AppWindow* host) : host_(host) {}
163
164AshPanelContents::~AshPanelContents() {
165}
166
167void AshPanelContents::Initialize(content::BrowserContext* context,
168                                  const GURL& url) {
169  url_ = url;
170
171  extension_function_dispatcher_.reset(
172      new extensions::ExtensionFunctionDispatcher(context, this));
173
174  web_contents_.reset(
175      content::WebContents::Create(content::WebContents::CreateParams(
176          context, content::SiteInstance::CreateForURL(context, url_))));
177
178  // Needed to give the web contents a Window ID. Extension APIs expect web
179  // contents to have a Window ID. Also required for FaviconTabHelper to
180  // correctly set the window icon and title.
181  SessionTabHelper::CreateForWebContents(web_contents_.get());
182  SessionTabHelper::FromWebContents(web_contents_.get())->SetWindowID(
183      host_->session_id());
184
185  // Responsible for loading favicons for the Launcher, which uses different
186  // logic than the FaviconTabHelper associated with web_contents_
187  // (instantiated in AppWindow::Init())
188  launcher_favicon_loader_.reset(
189      new LauncherFaviconLoader(this, web_contents_.get()));
190
191  content::WebContentsObserver::Observe(web_contents_.get());
192}
193
194void AshPanelContents::LoadContents(int32 creator_process_id) {
195  // This must be created after the native window has been created.
196  window_controller_.reset(new AshPanelWindowController(
197      host_, Profile::FromBrowserContext(host_->browser_context())));
198
199  web_contents_->GetController().LoadURL(
200      url_, content::Referrer(), ui::PAGE_TRANSITION_LINK,
201      std::string());
202}
203
204void AshPanelContents::NativeWindowChanged(NativeAppWindow* native_app_window) {
205  if (window_controller_)
206    window_controller_->NativeWindowChanged();
207}
208
209void AshPanelContents::NativeWindowClosed() {
210}
211
212void AshPanelContents::DispatchWindowShownForTests() const {
213}
214
215content::WebContents* AshPanelContents::GetWebContents() const {
216  return web_contents_.get();
217}
218
219void AshPanelContents::FaviconUpdated() {
220  gfx::Image new_image = gfx::Image::CreateFrom1xBitmap(
221      launcher_favicon_loader_->GetFavicon());
222  host_->UpdateAppIcon(new_image);
223}
224
225bool AshPanelContents::OnMessageReceived(const IPC::Message& message) {
226  bool handled = true;
227  IPC_BEGIN_MESSAGE_MAP(AshPanelContents, message)
228    IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
229    IPC_MESSAGE_UNHANDLED(handled = false)
230  IPC_END_MESSAGE_MAP()
231  return handled;
232}
233
234extensions::WindowController*
235AshPanelContents::GetExtensionWindowController() const {
236  return window_controller_.get();
237}
238
239content::WebContents* AshPanelContents::GetAssociatedWebContents() const {
240  return web_contents_.get();
241}
242
243void AshPanelContents::OnRequest(
244    const ExtensionHostMsg_Request_Params& params) {
245  extension_function_dispatcher_->Dispatch(
246      params, web_contents_->GetRenderViewHost());
247}
248