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 "content/public/browser/browser_context.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 "extensions/common/extension_messages.h" 22 #include "ui/gfx/image/image.h" 23 24 using apps::AppWindow; 25 using apps::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. 33 class 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 60 AshPanelWindowController::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 68 AshPanelWindowController::~AshPanelWindowController() { 69 extensions::WindowControllerList::GetInstance()->RemoveExtensionWindow(this); 70 } 71 72 int AshPanelWindowController::GetWindowId() const { 73 return static_cast<int>(app_window_->session_id().id()); 74 } 75 76 std::string AshPanelWindowController::GetWindowTypeText() const { 77 return extensions::tabs_constants::kWindowTypeValuePanel; 78 } 79 80 base::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 93 base::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 SessionID::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 132 bool AshPanelWindowController::CanClose(Reason* reason) const { 133 return true; 134 } 135 136 void AshPanelWindowController::SetFullscreenMode( 137 bool is_fullscreen, const GURL& extension_url) const { 138 // Do nothing. Panels cannot be fullscreen. 139 } 140 141 bool AshPanelWindowController::IsVisibleToExtension( 142 const extensions::Extension* extension) const { 143 return extension->id() == app_window_->extension_id(); 144 } 145 146 void 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 162 AshPanelContents::AshPanelContents(AppWindow* host) : host_(host) {} 163 164 AshPanelContents::~AshPanelContents() { 165 } 166 167 void 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 194 void 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(), content::PAGE_TRANSITION_LINK, 201 std::string()); 202 } 203 204 void AshPanelContents::NativeWindowChanged(NativeAppWindow* native_app_window) { 205 if (window_controller_) 206 window_controller_->NativeWindowChanged(); 207 } 208 209 void AshPanelContents::NativeWindowClosed() { 210 } 211 212 void AshPanelContents::DispatchWindowShownForTests() const { 213 } 214 215 content::WebContents* AshPanelContents::GetWebContents() const { 216 return web_contents_.get(); 217 } 218 219 void AshPanelContents::FaviconUpdated() { 220 gfx::Image new_image = gfx::Image::CreateFrom1xBitmap( 221 launcher_favicon_loader_->GetFavicon()); 222 host_->UpdateAppIcon(new_image); 223 } 224 225 bool 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 234 extensions::WindowController* 235 AshPanelContents::GetExtensionWindowController() const { 236 return window_controller_.get(); 237 } 238 239 content::WebContents* AshPanelContents::GetAssociatedWebContents() const { 240 return web_contents_.get(); 241 } 242 243 void AshPanelContents::OnRequest( 244 const ExtensionHostMsg_Request_Params& params) { 245 extension_function_dispatcher_->Dispatch( 246 params, web_contents_->GetRenderViewHost()); 247 } 248