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