| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/ui/webui/ntp/app_launcher_handler.h" | 5 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/auto_reset.h" | 10 #include "base/auto_reset.h" |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 46 #include "grit/browser_resources.h" | 46 #include "grit/browser_resources.h" |
| 47 #include "grit/generated_resources.h" | 47 #include "grit/generated_resources.h" |
| 48 #include "net/base/escape.h" | 48 #include "net/base/escape.h" |
| 49 #include "ui/base/animation/animation.h" | 49 #include "ui/base/animation/animation.h" |
| 50 #include "ui/base/l10n/l10n_util.h" | 50 #include "ui/base/l10n/l10n_util.h" |
| 51 #include "ui/gfx/codec/png_codec.h" | 51 #include "ui/gfx/codec/png_codec.h" |
| 52 #include "webkit/glue/window_open_disposition.h" | 52 #include "webkit/glue/window_open_disposition.h" |
| 53 | 53 |
| 54 namespace { | 54 namespace { |
| 55 | 55 |
| 56 // The URL prefixes used by the NTP to signal when the web store or an app | |
| 57 // has launched so we can record the proper histogram. | |
| 58 const char* kPingLaunchAppByID = "record-app-launch-by-id"; | |
| 59 const char* kPingLaunchWebStore = "record-webstore-launch"; | |
| 60 const char* kPingLaunchAppByURL = "record-app-launch-by-url"; | |
| 61 | |
| 62 const UnescapeRule::Type kUnescapeRules = | 56 const UnescapeRule::Type kUnescapeRules = |
| 63 UnescapeRule::NORMAL | UnescapeRule::URL_SPECIAL_CHARS; | 57 UnescapeRule::NORMAL | UnescapeRule::URL_SPECIAL_CHARS; |
| 64 | 58 |
| 65 extension_misc::AppLaunchBucket ParseLaunchSource( | 59 extension_misc::AppLaunchBucket ParseLaunchSource( |
| 66 const std::string& launch_source) { | 60 const std::string& launch_source) { |
| 67 int bucket_num = extension_misc::APP_LAUNCH_BUCKET_INVALID; | 61 int bucket_num = extension_misc::APP_LAUNCH_BUCKET_INVALID; |
| 68 base::StringToInt(launch_source, &bucket_num); | 62 base::StringToInt(launch_source, &bucket_num); |
| 69 extension_misc::AppLaunchBucket bucket = | 63 extension_misc::AppLaunchBucket bucket = |
| 70 static_cast<extension_misc::AppLaunchBucket>(bucket_num); | 64 static_cast<extension_misc::AppLaunchBucket>(bucket_num); |
| 71 CHECK(bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); | 65 CHECK(bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| 72 return bucket; | 66 return bucket; |
| 73 } | 67 } |
| 74 | 68 |
| 75 } // namespace | 69 } // namespace |
| 76 | 70 |
| 77 AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service) | 71 AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service) |
| 78 : extension_service_(extension_service), | 72 : extension_service_(extension_service), |
| 79 promo_active_(false), | |
| 80 ignore_changes_(false), | 73 ignore_changes_(false), |
| 81 attempted_bookmark_app_install_(false), | 74 attempted_bookmark_app_install_(false), |
| 82 has_loaded_apps_(false) { | 75 has_loaded_apps_(false) { |
| 83 } | 76 } |
| 84 | 77 |
| 85 AppLauncherHandler::~AppLauncherHandler() {} | 78 AppLauncherHandler::~AppLauncherHandler() {} |
| 86 | 79 |
| 87 // Serializes |notification| into a new DictionaryValue which the caller then | 80 // Serializes |notification| into a new DictionaryValue which the caller then |
| 88 // owns. | 81 // owns. |
| 89 static DictionaryValue* SerializeNotification( | 82 static DictionaryValue* SerializeNotification( |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 if (page_index < 0) { | 176 if (page_index < 0) { |
| 184 // Make sure every app has a page index (some predate the page index). | 177 // Make sure every app has a page index (some predate the page index). |
| 185 // The webstore app should be on the first page. | 178 // The webstore app should be on the first page. |
| 186 page_index = extension->id() == extension_misc::kWebStoreAppId ? | 179 page_index = extension->id() == extension_misc::kWebStoreAppId ? |
| 187 0 : prefs->GetNaturalAppPageIndex(); | 180 0 : prefs->GetNaturalAppPageIndex(); |
| 188 prefs->SetPageIndex(extension->id(), page_index); | 181 prefs->SetPageIndex(extension->id(), page_index); |
| 189 } | 182 } |
| 190 value->SetInteger("page_index", page_index); | 183 value->SetInteger("page_index", page_index); |
| 191 } | 184 } |
| 192 | 185 |
| 193 // TODO(estade): remove this. We record app launches via js calls rather than | |
| 194 // pings for ntp4. | |
| 195 // static | |
| 196 bool AppLauncherHandler::HandlePing(Profile* profile, const std::string& path) { | |
| 197 std::vector<std::string> params; | |
| 198 base::SplitString(path, '+', ¶ms); | |
| 199 | |
| 200 // Check if the user launched an app from the most visited or recently | |
| 201 // closed sections. | |
| 202 if (kPingLaunchAppByURL == params.at(0)) { | |
| 203 CHECK(params.size() == 3); | |
| 204 RecordAppLaunchByURL( | |
| 205 profile, params.at(1), ParseLaunchSource(params.at(2))); | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 bool is_web_store_ping = kPingLaunchWebStore == params.at(0); | |
| 210 bool is_app_launch_ping = kPingLaunchAppByID == params.at(0); | |
| 211 | |
| 212 if (!is_web_store_ping && !is_app_launch_ping) | |
| 213 return false; | |
| 214 | |
| 215 CHECK(params.size() >= 2); | |
| 216 | |
| 217 bool is_promo_active = params.at(1) == "true"; | |
| 218 | |
| 219 // At this point, the user must have used the app launcher, so we hide the | |
| 220 // promo if its still displayed. | |
| 221 if (is_promo_active) { | |
| 222 DCHECK(profile->GetExtensionService()); | |
| 223 profile->GetExtensionService()->apps_promo()->ExpireDefaultApps(); | |
| 224 } | |
| 225 | |
| 226 if (is_web_store_ping) { | |
| 227 RecordWebStoreLaunch(is_promo_active); | |
| 228 } else { | |
| 229 CHECK(params.size() == 3); | |
| 230 RecordAppLaunchByID(is_promo_active, ParseLaunchSource(params.at(2))); | |
| 231 } | |
| 232 | |
| 233 return true; | |
| 234 } | |
| 235 | |
| 236 WebUIMessageHandler* AppLauncherHandler::Attach(WebUI* web_ui) { | 186 WebUIMessageHandler* AppLauncherHandler::Attach(WebUI* web_ui) { |
| 237 registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_NTP, | 187 registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_NTP, |
| 238 content::Source<TabContents>(web_ui->tab_contents())); | 188 content::Source<TabContents>(web_ui->tab_contents())); |
| 239 return WebUIMessageHandler::Attach(web_ui); | 189 return WebUIMessageHandler::Attach(web_ui); |
| 240 } | 190 } |
| 241 | 191 |
| 242 void AppLauncherHandler::RegisterMessages() { | 192 void AppLauncherHandler::RegisterMessages() { |
| 243 web_ui_->RegisterMessageCallback("getApps", | 193 web_ui_->RegisterMessageCallback("getApps", |
| 244 base::Bind(&AppLauncherHandler::HandleGetApps, | 194 base::Bind(&AppLauncherHandler::HandleGetApps, |
| 245 base::Unretained(this))); | 195 base::Unretained(this))); |
| (...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 // expired. | 457 // expired. |
| 508 // b) Conceptually, it doesn't really make sense to count a | 458 // b) Conceptually, it doesn't really make sense to count a |
| 509 // prefchange-triggered refresh as a promo 'view'. | 459 // prefchange-triggered refresh as a promo 'view'. |
| 510 AppsPromo* apps_promo = extension_service_->apps_promo(); | 460 AppsPromo* apps_promo = extension_service_->apps_promo(); |
| 511 Profile* profile = Profile::FromWebUI(web_ui_); | 461 Profile* profile = Profile::FromWebUI(web_ui_); |
| 512 bool apps_promo_just_expired = false; | 462 bool apps_promo_just_expired = false; |
| 513 if (apps_promo->ShouldShowPromo(extension_service_->GetAppIds(), | 463 if (apps_promo->ShouldShowPromo(extension_service_->GetAppIds(), |
| 514 &apps_promo_just_expired)) { | 464 &apps_promo_just_expired)) { |
| 515 dictionary.SetBoolean("showPromo", true); | 465 dictionary.SetBoolean("showPromo", true); |
| 516 FillPromoDictionary(&dictionary); | 466 FillPromoDictionary(&dictionary); |
| 517 promo_active_ = true; | |
| 518 } else { | 467 } else { |
| 519 dictionary.SetBoolean("showPromo", false); | 468 dictionary.SetBoolean("showPromo", false); |
| 520 promo_active_ = false; | |
| 521 } | 469 } |
| 522 | 470 |
| 523 // If the default apps have just expired (user viewed them too many times with | 471 // If the default apps have just expired (user viewed them too many times with |
| 524 // no interaction), then we uninstall them and focus the recent sites section. | 472 // no interaction), then we uninstall them and focus the recent sites section. |
| 525 if (apps_promo_just_expired) { | 473 if (apps_promo_just_expired) { |
| 526 ignore_changes_ = true; | 474 ignore_changes_ = true; |
| 527 UninstallDefaultApps(); | 475 UninstallDefaultApps(); |
| 528 ignore_changes_ = false; | 476 ignore_changes_ = false; |
| 529 } | 477 } |
| 530 | 478 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 555 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, | 503 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, |
| 556 content::Source<Profile>(profile)); | 504 content::Source<Profile>(profile)); |
| 557 } | 505 } |
| 558 | 506 |
| 559 has_loaded_apps_ = true; | 507 has_loaded_apps_ = true; |
| 560 } | 508 } |
| 561 | 509 |
| 562 void AppLauncherHandler::HandleLaunchApp(const ListValue* args) { | 510 void AppLauncherHandler::HandleLaunchApp(const ListValue* args) { |
| 563 std::string extension_id; | 511 std::string extension_id; |
| 564 double source = -1.0; | 512 double source = -1.0; |
| 513 std::string url; |
| 565 bool alt_key = false; | 514 bool alt_key = false; |
| 566 bool ctrl_key = false; | 515 bool ctrl_key = false; |
| 567 bool meta_key = false; | 516 bool meta_key = false; |
| 568 bool shift_key = false; | 517 bool shift_key = false; |
| 569 double button = 0.0; | 518 double button = 0.0; |
| 570 | 519 |
| 571 CHECK(args->GetString(0, &extension_id)); | 520 CHECK(args->GetString(0, &extension_id)); |
| 572 CHECK(args->GetDouble(1, &source)); | 521 CHECK(args->GetDouble(1, &source)); |
| 573 if (args->GetSize() > 2) { | 522 if (args->GetSize() > 2) |
| 574 CHECK(args->GetBoolean(2, &alt_key)); | 523 CHECK(args->GetString(2, &url)); |
| 575 CHECK(args->GetBoolean(3, &ctrl_key)); | 524 if (args->GetSize() > 3) { |
| 576 CHECK(args->GetBoolean(4, &meta_key)); | 525 CHECK(args->GetBoolean(3, &alt_key)); |
| 577 CHECK(args->GetBoolean(5, &shift_key)); | 526 CHECK(args->GetBoolean(4, &ctrl_key)); |
| 578 CHECK(args->GetDouble(6, &button)); | 527 CHECK(args->GetBoolean(5, &meta_key)); |
| 528 CHECK(args->GetBoolean(6, &shift_key)); |
| 529 CHECK(args->GetDouble(7, &button)); |
| 579 } | 530 } |
| 580 | 531 |
| 581 extension_misc::AppLaunchBucket launch_bucket = | 532 extension_misc::AppLaunchBucket launch_bucket = |
| 582 static_cast<extension_misc::AppLaunchBucket>( | 533 static_cast<extension_misc::AppLaunchBucket>( |
| 583 static_cast<int>(source)); | 534 static_cast<int>(source)); |
| 584 CHECK(launch_bucket >= 0 && | 535 CHECK(launch_bucket >= 0 && |
| 585 launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); | 536 launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| 586 | 537 |
| 587 const Extension* extension = | 538 const Extension* extension = |
| 588 extension_service_->GetExtensionById(extension_id, false); | 539 extension_service_->GetExtensionById(extension_id, false); |
| 589 | 540 |
| 590 // Prompt the user to re-enable the application if disabled. | 541 // Prompt the user to re-enable the application if disabled. |
| 591 if (!extension) { | 542 if (!extension) { |
| 592 PromptToEnableApp(extension_id); | 543 PromptToEnableApp(extension_id); |
| 593 return; | 544 return; |
| 594 } | 545 } |
| 595 | 546 |
| 596 Profile* profile = extension_service_->profile(); | 547 Profile* profile = extension_service_->profile(); |
| 597 | 548 |
| 598 // If the user pressed special keys when clicking, override the saved | 549 // If the user pressed special keys when clicking, override the saved |
| 599 // preference for launch container. | 550 // preference for launch container. |
| 600 bool middle_button = (button == 1.0); | 551 bool middle_button = (button == 1.0); |
| 601 WindowOpenDisposition disposition = | 552 WindowOpenDisposition disposition = |
| 602 disposition_utils::DispositionFromClick(middle_button, alt_key, | 553 disposition_utils::DispositionFromClick(middle_button, alt_key, |
| 603 ctrl_key, meta_key, shift_key); | 554 ctrl_key, meta_key, shift_key); |
| 604 | 555 |
| 605 if (extension_id != extension_misc::kWebStoreAppId) { | 556 if (extension_id != extension_misc::kWebStoreAppId) { |
| 606 RecordAppLaunchByID(promo_active_, launch_bucket); | 557 RecordAppLaunchByID(launch_bucket); |
| 607 extension_service_->apps_promo()->ExpireDefaultApps(); | 558 extension_service_->apps_promo()->ExpireDefaultApps(); |
| 608 } else if (NewTabUI::NTP4Enabled()) { | 559 } else if (NewTabUI::NTP4Enabled()) { |
| 609 RecordWebStoreLaunch(promo_active_); | 560 RecordWebStoreLaunch(url.find("chrome-ntp-promo") != std::string::npos); |
| 610 } | 561 } |
| 611 | 562 |
| 612 if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { | 563 if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { |
| 613 // TODO(jamescook): Proper support for background tabs. | 564 // TODO(jamescook): Proper support for background tabs. |
| 614 Browser::OpenApplication( | 565 Browser::OpenApplication( |
| 615 profile, extension, extension_misc::LAUNCH_TAB, disposition); | 566 profile, extension, extension_misc::LAUNCH_TAB, GURL(url), disposition); |
| 616 } else if (disposition == NEW_WINDOW) { | 567 } else if (disposition == NEW_WINDOW) { |
| 617 // Force a new window open. | 568 // Force a new window open. |
| 618 Browser::OpenApplication( | 569 Browser::OpenApplication( |
| 619 profile, extension, extension_misc::LAUNCH_WINDOW, disposition); | 570 profile, extension, extension_misc::LAUNCH_WINDOW, GURL(url), |
| 571 disposition); |
| 620 } else { | 572 } else { |
| 621 // Look at preference to find the right launch container. If no preference | 573 // Look at preference to find the right launch container. If no preference |
| 622 // is set, launch as a regular tab. | 574 // is set, launch as a regular tab. |
| 623 extension_misc::LaunchContainer launch_container = | 575 extension_misc::LaunchContainer launch_container = |
| 624 extension_service_->extension_prefs()->GetLaunchContainer( | 576 extension_service_->extension_prefs()->GetLaunchContainer( |
| 625 extension, ExtensionPrefs::LAUNCH_REGULAR); | 577 extension, ExtensionPrefs::LAUNCH_REGULAR); |
| 626 | 578 |
| 627 // To give a more "launchy" experience when using the NTP launcher, we close | 579 // To give a more "launchy" experience when using the NTP launcher, we close |
| 628 // it automatically. | 580 // it automatically. |
| 629 Browser* browser = BrowserList::GetLastActiveWithProfile(profile); | 581 Browser* browser = BrowserList::GetLastActiveWithProfile(profile); |
| 630 TabContents* old_contents = NULL; | 582 TabContents* old_contents = NULL; |
| 631 if (browser) | 583 if (browser) |
| 632 old_contents = browser->GetSelectedTabContents(); | 584 old_contents = browser->GetSelectedTabContents(); |
| 633 | 585 |
| 634 TabContents* new_contents = Browser::OpenApplication( | 586 TabContents* new_contents = Browser::OpenApplication( |
| 635 profile, extension, launch_container, | 587 profile, extension, launch_container, GURL(url), |
| 636 old_contents ? CURRENT_TAB : NEW_FOREGROUND_TAB); | 588 old_contents ? CURRENT_TAB : NEW_FOREGROUND_TAB); |
| 637 | 589 |
| 638 // This will also destroy the handler, so do not perform any actions after. | 590 // This will also destroy the handler, so do not perform any actions after. |
| 639 if (new_contents != old_contents && browser && browser->tab_count() > 1) | 591 if (new_contents != old_contents && browser && browser->tab_count() > 1) |
| 640 browser->CloseTabContents(old_contents); | 592 browser->CloseTabContents(old_contents); |
| 641 } | 593 } |
| 642 } | 594 } |
| 643 | 595 |
| 644 void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { | 596 void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { |
| 645 std::string extension_id; | 597 std::string extension_id; |
| (...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 903 | 855 |
| 904 if (!promo_active) return; | 856 if (!promo_active) return; |
| 905 | 857 |
| 906 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, | 858 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| 907 extension_misc::PROMO_LAUNCH_WEB_STORE, | 859 extension_misc::PROMO_LAUNCH_WEB_STORE, |
| 908 extension_misc::PROMO_BUCKET_BOUNDARY); | 860 extension_misc::PROMO_BUCKET_BOUNDARY); |
| 909 } | 861 } |
| 910 | 862 |
| 911 // static | 863 // static |
| 912 void AppLauncherHandler::RecordAppLaunchByID( | 864 void AppLauncherHandler::RecordAppLaunchByID( |
| 913 bool promo_active, extension_misc::AppLaunchBucket bucket) { | 865 extension_misc::AppLaunchBucket bucket) { |
| 914 CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID); | 866 CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID); |
| 915 | 867 |
| 916 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, bucket, | 868 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, bucket, |
| 917 extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); | 869 extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| 918 | |
| 919 if (!promo_active) return; | |
| 920 | |
| 921 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, | |
| 922 extension_misc::PROMO_LAUNCH_APP, | |
| 923 extension_misc::PROMO_BUCKET_BOUNDARY); | |
| 924 } | 870 } |
| 925 | 871 |
| 926 // static | 872 // static |
| 927 void AppLauncherHandler::RecordAppLaunchByURL( | 873 void AppLauncherHandler::RecordAppLaunchByURL( |
| 928 Profile* profile, | 874 Profile* profile, |
| 929 std::string escaped_url, | 875 std::string escaped_url, |
| 930 extension_misc::AppLaunchBucket bucket) { | 876 extension_misc::AppLaunchBucket bucket) { |
| 931 CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID); | 877 CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID); |
| 932 | 878 |
| 933 GURL url(net::UnescapeURLComponent(escaped_url, kUnescapeRules)); | 879 GURL url(net::UnescapeURLComponent(escaped_url, kUnescapeRules)); |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1049 | 995 |
| 1050 void AppLauncherHandler::UninstallDefaultApps() { | 996 void AppLauncherHandler::UninstallDefaultApps() { |
| 1051 AppsPromo* apps_promo = extension_service_->apps_promo(); | 997 AppsPromo* apps_promo = extension_service_->apps_promo(); |
| 1052 const ExtensionIdSet& app_ids = apps_promo->old_default_apps(); | 998 const ExtensionIdSet& app_ids = apps_promo->old_default_apps(); |
| 1053 for (ExtensionIdSet::const_iterator iter = app_ids.begin(); | 999 for (ExtensionIdSet::const_iterator iter = app_ids.begin(); |
| 1054 iter != app_ids.end(); ++iter) { | 1000 iter != app_ids.end(); ++iter) { |
| 1055 if (extension_service_->GetExtensionById(*iter, true)) | 1001 if (extension_service_->GetExtensionById(*iter, true)) |
| 1056 extension_service_->UninstallExtension(*iter, false, NULL); | 1002 extension_service_->UninstallExtension(*iter, false, NULL); |
| 1057 } | 1003 } |
| 1058 } | 1004 } |
| OLD | NEW |