OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/dom_ui/app_launcher_handler.h" | 5 #include "chrome/browser/dom_ui/app_launcher_handler.h" |
6 | 6 |
7 #include "app/animation.h" | 7 #include "app/animation.h" |
| 8 #include "base/metrics/histogram.h" |
8 #include "base/string_number_conversions.h" | 9 #include "base/string_number_conversions.h" |
| 10 #include "base/string_split.h" |
9 #include "base/string_util.h" | 11 #include "base/string_util.h" |
10 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
11 #include "base/values.h" | 13 #include "base/values.h" |
12 #include "chrome/browser/app_launched_animation.h" | 14 #include "chrome/browser/app_launched_animation.h" |
13 #include "chrome/browser/browser.h" | 15 #include "chrome/browser/browser.h" |
14 #include "chrome/browser/browser_list.h" | 16 #include "chrome/browser/browser_list.h" |
15 #include "chrome/browser/extensions/default_apps.h" | 17 #include "chrome/browser/extensions/default_apps.h" |
16 #include "chrome/browser/extensions/extension_prefs.h" | 18 #include "chrome/browser/extensions/extension_prefs.h" |
17 #include "chrome/browser/extensions/extensions_service.h" | 19 #include "chrome/browser/extensions/extensions_service.h" |
18 #include "chrome/browser/platform_util.h" | 20 #include "chrome/browser/platform_util.h" |
19 #include "chrome/browser/profile.h" | 21 #include "chrome/browser/profile.h" |
20 #include "chrome/browser/tab_contents/tab_contents.h" | 22 #include "chrome/browser/tab_contents/tab_contents.h" |
21 #include "chrome/common/chrome_switches.h" | 23 #include "chrome/common/chrome_switches.h" |
22 #include "chrome/common/extensions/extension.h" | 24 #include "chrome/common/extensions/extension.h" |
23 #include "chrome/common/extensions/extension_constants.h" | 25 #include "chrome/common/extensions/extension_constants.h" |
24 #include "chrome/common/extensions/extension_icon_set.h" | 26 #include "chrome/common/extensions/extension_icon_set.h" |
25 #include "chrome/common/extensions/extension_resource.h" | 27 #include "chrome/common/extensions/extension_resource.h" |
26 #include "chrome/common/notification_service.h" | 28 #include "chrome/common/notification_service.h" |
27 #include "chrome/common/notification_type.h" | 29 #include "chrome/common/notification_type.h" |
28 #include "chrome/common/url_constants.h" | 30 #include "chrome/common/url_constants.h" |
29 #include "gfx/rect.h" | 31 #include "gfx/rect.h" |
30 #include "grit/browser_resources.h" | 32 #include "grit/browser_resources.h" |
31 #include "grit/generated_resources.h" | 33 #include "grit/generated_resources.h" |
32 | 34 |
33 namespace { | 35 namespace { |
34 | 36 |
| 37 // The URL prefixes used by the NTP to signal when the web store or an app |
| 38 // has launched. These are used for histogram purposes. |
| 39 const char* kLaunchAppPingURL = "record-app-launch"; |
| 40 const char* kLaunchWebStorePingURL = "record-webstore-launch"; |
| 41 |
35 // This extracts an int from a ListValue at the given |index|. | 42 // This extracts an int from a ListValue at the given |index|. |
36 bool ExtractInt(const ListValue* list, size_t index, int* out_int) { | 43 bool ExtractInt(const ListValue* list, size_t index, int* out_int) { |
37 std::string string_value; | 44 std::string string_value; |
38 | 45 |
39 if (list->GetString(index, &string_value)) { | 46 if (list->GetString(index, &string_value)) { |
40 base::StringToInt(string_value, out_int); | 47 base::StringToInt(string_value, out_int); |
41 return true; | 48 return true; |
42 } | 49 } |
43 | 50 |
44 return false; | 51 return false; |
45 } | 52 } |
46 | 53 |
47 std::string GetIconURL(const Extension* extension, Extension::Icons icon, | 54 std::string GetIconURL(const Extension* extension, Extension::Icons icon, |
48 const std::string& default_val) { | 55 const std::string& default_val) { |
49 GURL url = extension->GetIconURL(icon, ExtensionIconSet::MATCH_EXACTLY); | 56 GURL url = extension->GetIconURL(icon, ExtensionIconSet::MATCH_EXACTLY); |
50 if (!url.is_empty()) | 57 if (!url.is_empty()) |
51 return url.spec(); | 58 return url.spec(); |
52 else | 59 else |
53 return default_val; | 60 return default_val; |
54 } | 61 } |
55 | 62 |
| 63 // Extracts the promo parameter from the |path| generated by a ping on the NTP. |
| 64 bool IsPromoActive(const std::string& path) { |
| 65 std::vector<std::string> params; |
| 66 base::SplitString(path, '+', ¶ms); |
| 67 |
| 68 CHECK(params.size() == 2); |
| 69 |
| 70 return params.at(1) == "true"; |
| 71 } |
| 72 |
56 } // namespace | 73 } // namespace |
57 | 74 |
58 AppLauncherHandler::AppLauncherHandler(ExtensionsService* extension_service) | 75 AppLauncherHandler::AppLauncherHandler(ExtensionsService* extension_service) |
59 : extensions_service_(extension_service) { | 76 : extensions_service_(extension_service), |
| 77 promo_active_(false) { |
60 } | 78 } |
61 | 79 |
62 AppLauncherHandler::~AppLauncherHandler() {} | 80 AppLauncherHandler::~AppLauncherHandler() {} |
63 | 81 |
| 82 // static |
| 83 void AppLauncherHandler::CreateAppInfo(const Extension* extension, |
| 84 ExtensionPrefs* extension_prefs, |
| 85 DictionaryValue* value) { |
| 86 value->Clear(); |
| 87 value->SetString("id", extension->id()); |
| 88 value->SetString("name", extension->name()); |
| 89 value->SetString("description", extension->description()); |
| 90 value->SetString("launch_url", extension->GetFullLaunchURL().spec()); |
| 91 value->SetString("options_url", extension->options_url().spec()); |
| 92 value->SetString("icon_big", GetIconURL( |
| 93 extension, Extension::EXTENSION_ICON_LARGE, |
| 94 "chrome://theme/IDR_APP_DEFAULT_ICON")); |
| 95 value->SetString("icon_small", GetIconURL( |
| 96 extension, Extension::EXTENSION_ICON_BITTY, |
| 97 std::string("chrome://favicon/") + extension->GetFullLaunchURL().spec())); |
| 98 value->SetInteger("launch_container", extension->launch_container()); |
| 99 value->SetInteger("launch_type", |
| 100 extension_prefs->GetLaunchType(extension->id())); |
| 101 |
| 102 int app_launch_index = extension_prefs->GetAppLaunchIndex(extension->id()); |
| 103 if (app_launch_index == -1) { |
| 104 // Make sure every app has a launch index (some predate the launch index). |
| 105 app_launch_index = extension_prefs->GetNextAppLaunchIndex(); |
| 106 extension_prefs->SetAppLaunchIndex(extension->id(), app_launch_index); |
| 107 } |
| 108 value->SetInteger("app_launch_index", app_launch_index); |
| 109 } |
| 110 |
| 111 // static |
| 112 bool AppLauncherHandler::HandlePing(const std::string& path) { |
| 113 if (path.find(kLaunchWebStorePingURL) != std::string::npos) { |
| 114 RecordWebStoreLaunch(IsPromoActive(path)); |
| 115 return true; |
| 116 } else if (path.find(kLaunchAppPingURL) != std::string::npos) { |
| 117 RecordAppLaunch(IsPromoActive(path)); |
| 118 return true; |
| 119 } |
| 120 |
| 121 return false; |
| 122 } |
| 123 |
64 DOMMessageHandler* AppLauncherHandler::Attach(DOMUI* dom_ui) { | 124 DOMMessageHandler* AppLauncherHandler::Attach(DOMUI* dom_ui) { |
65 // TODO(arv): Add initialization code to the Apps store etc. | 125 // TODO(arv): Add initialization code to the Apps store etc. |
66 return DOMMessageHandler::Attach(dom_ui); | 126 return DOMMessageHandler::Attach(dom_ui); |
67 } | 127 } |
68 | 128 |
69 void AppLauncherHandler::RegisterMessages() { | 129 void AppLauncherHandler::RegisterMessages() { |
70 dom_ui_->RegisterMessageCallback("getApps", | 130 dom_ui_->RegisterMessageCallback("getApps", |
71 NewCallback(this, &AppLauncherHandler::HandleGetApps)); | 131 NewCallback(this, &AppLauncherHandler::HandleGetApps)); |
72 dom_ui_->RegisterMessageCallback("launchApp", | 132 dom_ui_->RegisterMessageCallback("launchApp", |
73 NewCallback(this, &AppLauncherHandler::HandleLaunchApp)); | 133 NewCallback(this, &AppLauncherHandler::HandleLaunchApp)); |
(...skipping 21 matching lines...) Expand all Loading... |
95 DictionaryValue dictionary; | 155 DictionaryValue dictionary; |
96 FillAppDictionary(&dictionary); | 156 FillAppDictionary(&dictionary); |
97 dom_ui_->CallJavascriptFunction(L"appsPrefChangeCallback", dictionary); | 157 dom_ui_->CallJavascriptFunction(L"appsPrefChangeCallback", dictionary); |
98 break; | 158 break; |
99 } | 159 } |
100 default: | 160 default: |
101 NOTREACHED(); | 161 NOTREACHED(); |
102 } | 162 } |
103 } | 163 } |
104 | 164 |
105 // static | |
106 void AppLauncherHandler::CreateAppInfo(const Extension* extension, | |
107 ExtensionPrefs* extension_prefs, | |
108 DictionaryValue* value) { | |
109 value->Clear(); | |
110 value->SetString("id", extension->id()); | |
111 value->SetString("name", extension->name()); | |
112 value->SetString("description", extension->description()); | |
113 value->SetString("launch_url", extension->GetFullLaunchURL().spec()); | |
114 value->SetString("options_url", extension->options_url().spec()); | |
115 value->SetString("icon_big", GetIconURL( | |
116 extension, Extension::EXTENSION_ICON_LARGE, | |
117 "chrome://theme/IDR_APP_DEFAULT_ICON")); | |
118 value->SetString("icon_small", GetIconURL( | |
119 extension, Extension::EXTENSION_ICON_BITTY, | |
120 std::string("chrome://favicon/") + extension->GetFullLaunchURL().spec())); | |
121 value->SetInteger("launch_container", extension->launch_container()); | |
122 value->SetInteger("launch_type", | |
123 extension_prefs->GetLaunchType(extension->id())); | |
124 | |
125 int app_launch_index = extension_prefs->GetAppLaunchIndex(extension->id()); | |
126 if (app_launch_index == -1) { | |
127 // Make sure every app has a launch index (some predate the launch index). | |
128 app_launch_index = extension_prefs->GetNextAppLaunchIndex(); | |
129 extension_prefs->SetAppLaunchIndex(extension->id(), app_launch_index); | |
130 } | |
131 value->SetInteger("app_launch_index", app_launch_index); | |
132 } | |
133 | |
134 void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { | 165 void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { |
135 ListValue* list = new ListValue(); | 166 ListValue* list = new ListValue(); |
136 const ExtensionList* extensions = extensions_service_->extensions(); | 167 const ExtensionList* extensions = extensions_service_->extensions(); |
137 for (ExtensionList::const_iterator it = extensions->begin(); | 168 for (ExtensionList::const_iterator it = extensions->begin(); |
138 it != extensions->end(); ++it) { | 169 it != extensions->end(); ++it) { |
139 // Don't include the WebStore component app. The WebStore launcher | 170 // Don't include the WebStore component app. The WebStore launcher |
140 // gets special treatment in ntp/apps.js. | 171 // gets special treatment in ntp/apps.js. |
141 if ((*it)->is_app() && (*it)->id() != extension_misc::kWebStoreAppId) { | 172 if ((*it)->is_app() && (*it)->id() != extension_misc::kWebStoreAppId) { |
142 DictionaryValue* app_info = new DictionaryValue(); | 173 DictionaryValue* app_info = new DictionaryValue(); |
143 CreateAppInfo(*it, extensions_service_->extension_prefs(), app_info); | 174 CreateAppInfo(*it, extensions_service_->extension_prefs(), app_info); |
144 list->Append(app_info); | 175 list->Append(app_info); |
145 } | 176 } |
146 } | 177 } |
147 dictionary->Set("apps", list); | 178 dictionary->Set("apps", list); |
148 | 179 |
149 DefaultApps* default_apps = extensions_service_->default_apps(); | 180 DefaultApps* default_apps = extensions_service_->default_apps(); |
150 if (default_apps->ShouldShowPromo(extensions_service_->GetAppIds())) { | 181 if (default_apps->ShouldShowPromo(extensions_service_->GetAppIds())) { |
151 dictionary->SetBoolean("showPromo", true); | 182 dictionary->SetBoolean("showPromo", true); |
152 default_apps->DidShowPromo(); | 183 default_apps->DidShowPromo(); |
| 184 promo_active_ = true; |
153 } else { | 185 } else { |
154 dictionary->SetBoolean("showPromo", false); | 186 dictionary->SetBoolean("showPromo", false); |
| 187 promo_active_ = false; |
155 } | 188 } |
156 | 189 |
157 bool showLauncher = | 190 bool showLauncher = |
158 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppLauncher); | 191 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppLauncher); |
159 dictionary->SetBoolean("showLauncher", showLauncher); | 192 dictionary->SetBoolean("showLauncher", showLauncher); |
160 } | 193 } |
161 | 194 |
162 void AppLauncherHandler::HandleGetApps(const ListValue* args) { | 195 void AppLauncherHandler::HandleGetApps(const ListValue* args) { |
163 DictionaryValue dictionary; | 196 DictionaryValue dictionary; |
164 FillAppDictionary(&dictionary); | 197 FillAppDictionary(&dictionary); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 TabContents* old_contents = NULL; | 246 TabContents* old_contents = NULL; |
214 if (browser) | 247 if (browser) |
215 old_contents = browser->GetSelectedTabContents(); | 248 old_contents = browser->GetSelectedTabContents(); |
216 | 249 |
217 AnimateAppIcon(extension, rect); | 250 AnimateAppIcon(extension, rect); |
218 TabContents* new_contents = Browser::OpenApplication( | 251 TabContents* new_contents = Browser::OpenApplication( |
219 profile, extension, extension->launch_container(), old_contents); | 252 profile, extension, extension->launch_container(), old_contents); |
220 | 253 |
221 if (new_contents != old_contents && browser->tab_count() > 1) | 254 if (new_contents != old_contents && browser->tab_count() > 1) |
222 browser->CloseTabContents(old_contents); | 255 browser->CloseTabContents(old_contents); |
| 256 |
| 257 if (extension_id != extension_misc::kWebStoreAppId) |
| 258 RecordAppLaunch(promo_active_); |
223 } | 259 } |
224 | 260 |
225 void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { | 261 void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { |
226 std::string extension_id; | 262 std::string extension_id; |
227 int launch_type; | 263 int launch_type; |
228 if (!args->GetString(0, &extension_id) || | 264 if (!args->GetString(0, &extension_id) || |
229 !ExtractInt(args, 1, &launch_type)) { | 265 !ExtractInt(args, 1, &launch_type)) { |
230 NOTREACHED(); | 266 NOTREACHED(); |
231 return; | 267 return; |
232 } | 268 } |
233 | 269 |
234 const Extension* extension = | 270 const Extension* extension = |
235 extensions_service_->GetExtensionById(extension_id, false); | 271 extensions_service_->GetExtensionById(extension_id, false); |
236 DCHECK(extension); | 272 DCHECK(extension); |
237 | 273 |
238 extensions_service_->extension_prefs()->SetLaunchType( | 274 extensions_service_->extension_prefs()->SetLaunchType( |
239 extension_id, | 275 extension_id, |
240 static_cast<ExtensionPrefs::LaunchType>(launch_type)); | 276 static_cast<ExtensionPrefs::LaunchType>(launch_type)); |
241 } | 277 } |
242 | 278 |
243 void AppLauncherHandler::AnimateAppIcon(const Extension* extension, | |
244 const gfx::Rect& rect) { | |
245 // We make this check for the case of minimized windows, unit tests, etc. | |
246 if (platform_util::IsVisible(dom_ui_->tab_contents()->GetNativeView()) && | |
247 Animation::ShouldRenderRichAnimation()) { | |
248 #if defined(OS_WIN) | |
249 AppLaunchedAnimation::Show(extension, rect); | |
250 #else | |
251 NOTIMPLEMENTED(); | |
252 #endif | |
253 } | |
254 } | |
255 | |
256 void AppLauncherHandler::HandleUninstallApp(const ListValue* args) { | 279 void AppLauncherHandler::HandleUninstallApp(const ListValue* args) { |
257 std::string extension_id = WideToUTF8(ExtractStringValue(args)); | 280 std::string extension_id = WideToUTF8(ExtractStringValue(args)); |
258 const Extension* extension = extensions_service_->GetExtensionById( | 281 const Extension* extension = extensions_service_->GetExtensionById( |
259 extension_id, false); | 282 extension_id, false); |
260 if (!extension) | 283 if (!extension) |
261 return; | 284 return; |
262 | 285 |
263 if (!extension_id_prompting_.empty()) | 286 if (!extension_id_prompting_.empty()) |
264 return; // Only one prompt at a time. | 287 return; // Only one prompt at a time. |
265 | 288 |
266 extension_id_prompting_ = extension_id; | 289 extension_id_prompting_ = extension_id; |
267 GetExtensionInstallUI()->ConfirmUninstall(this, extension); | 290 GetExtensionInstallUI()->ConfirmUninstall(this, extension); |
268 } | 291 } |
269 | 292 |
270 void AppLauncherHandler::HandleHideAppsPromo(const ListValue* args) { | 293 void AppLauncherHandler::HandleHideAppsPromo(const ListValue* args) { |
271 // If the user has intentionally hidden the promotion, we'll uninstall all the | 294 // If the user has intentionally hidden the promotion, we'll uninstall all the |
272 // default apps (we know the user hasn't installed any apps on their own at | 295 // default apps (we know the user hasn't installed any apps on their own at |
273 // this point, or the promotion wouldn't have been shown). | 296 // this point, or the promotion wouldn't have been shown). |
| 297 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| 298 extension_misc::PROMO_CLOSE, |
| 299 extension_misc::PROMO_BUCKET_BOUNDARY); |
274 DefaultApps* default_apps = extensions_service_->default_apps(); | 300 DefaultApps* default_apps = extensions_service_->default_apps(); |
275 const ExtensionIdSet* app_ids = default_apps->GetDefaultApps(); | 301 const ExtensionIdSet* app_ids = default_apps->GetDefaultApps(); |
276 DCHECK(*app_ids == extensions_service_->GetAppIds()); | 302 DCHECK(*app_ids == extensions_service_->GetAppIds()); |
277 | 303 |
278 for (ExtensionIdSet::const_iterator iter = app_ids->begin(); | 304 for (ExtensionIdSet::const_iterator iter = app_ids->begin(); |
279 iter != app_ids->end(); ++iter) { | 305 iter != app_ids->end(); ++iter) { |
280 if (extensions_service_->GetExtensionById(*iter, true)) | 306 if (extensions_service_->GetExtensionById(*iter, true)) |
281 extensions_service_->UninstallExtension(*iter, false); | 307 extensions_service_->UninstallExtension(*iter, false); |
282 } | 308 } |
283 | 309 |
284 extensions_service_->default_apps()->SetPromoHidden(); | 310 extensions_service_->default_apps()->SetPromoHidden(); |
285 } | 311 } |
286 | 312 |
287 ExtensionInstallUI* AppLauncherHandler::GetExtensionInstallUI() { | 313 //static |
288 if (!install_ui_.get()) | 314 void AppLauncherHandler::RecordWebStoreLaunch(bool promo_active) { |
289 install_ui_.reset(new ExtensionInstallUI(dom_ui_->GetProfile())); | 315 if (!promo_active) return; |
290 return install_ui_.get(); | 316 |
| 317 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| 318 extension_misc::PROMO_LAUNCH_WEB_STORE, |
| 319 extension_misc::PROMO_BUCKET_BOUNDARY); |
| 320 } |
| 321 |
| 322 //static |
| 323 void AppLauncherHandler::RecordAppLaunch(bool promo_active) { |
| 324 // TODO(jstritar): record app launches that occur when the promo is not |
| 325 // active using a different histogram. |
| 326 |
| 327 if (!promo_active) return; |
| 328 |
| 329 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| 330 extension_misc::PROMO_LAUNCH_APP, |
| 331 extension_misc::PROMO_BUCKET_BOUNDARY); |
291 } | 332 } |
292 | 333 |
293 void AppLauncherHandler::InstallUIProceed() { | 334 void AppLauncherHandler::InstallUIProceed() { |
294 DCHECK(!extension_id_prompting_.empty()); | 335 DCHECK(!extension_id_prompting_.empty()); |
295 | 336 |
296 // The extension can be uninstalled in another window while the UI was | 337 // The extension can be uninstalled in another window while the UI was |
297 // showing. Do nothing in that case. | 338 // showing. Do nothing in that case. |
298 const Extension* extension = | 339 const Extension* extension = |
299 extensions_service_->GetExtensionById(extension_id_prompting_, true); | 340 extensions_service_->GetExtensionById(extension_id_prompting_, true); |
300 if (!extension) | 341 if (!extension) |
301 return; | 342 return; |
302 | 343 |
303 extensions_service_->UninstallExtension(extension_id_prompting_, | 344 extensions_service_->UninstallExtension(extension_id_prompting_, |
304 false /* external_uninstall */); | 345 false /* external_uninstall */); |
305 extension_id_prompting_ = ""; | 346 extension_id_prompting_ = ""; |
306 } | 347 } |
307 | 348 |
308 void AppLauncherHandler::InstallUIAbort() { | 349 void AppLauncherHandler::InstallUIAbort() { |
309 extension_id_prompting_ = ""; | 350 extension_id_prompting_ = ""; |
310 } | 351 } |
| 352 |
| 353 ExtensionInstallUI* AppLauncherHandler::GetExtensionInstallUI() { |
| 354 if (!install_ui_.get()) |
| 355 install_ui_.reset(new ExtensionInstallUI(dom_ui_->GetProfile())); |
| 356 return install_ui_.get(); |
| 357 } |
| 358 |
| 359 void AppLauncherHandler::AnimateAppIcon(const Extension* extension, |
| 360 const gfx::Rect& rect) { |
| 361 // We make this check for the case of minimized windows, unit tests, etc. |
| 362 if (platform_util::IsVisible(dom_ui_->tab_contents()->GetNativeView()) && |
| 363 Animation::ShouldRenderRichAnimation()) { |
| 364 #if defined(OS_WIN) |
| 365 AppLaunchedAnimation::Show(extension, rect); |
| 366 #else |
| 367 NOTIMPLEMENTED(); |
| 368 #endif |
| 369 } |
| 370 } |
OLD | NEW |