Home | History | Annotate | Download | only in search
      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/ui/app_list/search/app_result.h"
      6 
      7 #include "chrome/browser/extensions/extension_service.h"
      8 #include "chrome/browser/extensions/extension_util.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/browser/ui/app_list/app_context_menu.h"
     11 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
     12 #include "chrome/browser/ui/app_list/search/tokenized_string.h"
     13 #include "chrome/browser/ui/app_list/search/tokenized_string_match.h"
     14 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
     15 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
     16 #include "content/public/browser/user_metrics.h"
     17 #include "extensions/browser/extension_registry.h"
     18 #include "extensions/browser/extension_system.h"
     19 #include "extensions/browser/extension_system_provider.h"
     20 #include "extensions/browser/extensions_browser_client.h"
     21 #include "extensions/common/constants.h"
     22 #include "extensions/common/extension.h"
     23 #include "extensions/common/extension_icon_set.h"
     24 #include "extensions/common/manifest_handlers/icons_handler.h"
     25 #include "ui/gfx/color_utils.h"
     26 #include "ui/gfx/image/image_skia_operations.h"
     27 
     28 namespace app_list {
     29 
     30 AppResult::AppResult(Profile* profile,
     31                      const std::string& app_id,
     32                      AppListControllerDelegate* controller)
     33     : profile_(profile),
     34       app_id_(app_id),
     35       controller_(controller),
     36       extension_registry_(NULL) {
     37   set_id(extensions::Extension::GetBaseURLFromExtensionId(app_id_).spec());
     38 
     39   const extensions::Extension* extension =
     40       extensions::ExtensionSystem::Get(profile_)->extension_service()
     41           ->GetInstalledExtension(app_id_);
     42   DCHECK(extension);
     43 
     44   is_platform_app_ = extension->is_platform_app();
     45 
     46   icon_.reset(new extensions::IconImage(
     47       profile_,
     48       extension,
     49       extensions::IconsInfo::GetIcons(extension),
     50       extension_misc::EXTENSION_ICON_SMALL,
     51       extensions::util::GetDefaultAppIcon(),
     52       this));
     53   UpdateIcon();
     54 
     55   StartObservingExtensionRegistry();
     56 }
     57 
     58 AppResult::~AppResult() {
     59   StopObservingExtensionRegistry();
     60 }
     61 
     62 void AppResult::UpdateFromMatch(const TokenizedString& title,
     63                                 const TokenizedStringMatch& match) {
     64   const TokenizedStringMatch::Hits& hits = match.hits();
     65 
     66   Tags tags;
     67   tags.reserve(hits.size());
     68   for (size_t i = 0; i < hits.size(); ++i)
     69     tags.push_back(Tag(Tag::MATCH, hits[i].start(), hits[i].end()));
     70 
     71   set_title(title.text());
     72   set_title_tags(tags);
     73   set_relevance(match.relevance());
     74 }
     75 
     76 void AppResult::Open(int event_flags) {
     77   const extensions::Extension* extension =
     78       extensions::ExtensionSystem::Get(profile_)->extension_service()
     79           ->GetInstalledExtension(app_id_);
     80   if (!extension)
     81     return;
     82 
     83   // Check if enable flow is already running or should be started
     84   if (RunExtensionEnableFlow())
     85     return;
     86 
     87   CoreAppLauncherHandler::RecordAppListSearchLaunch(extension);
     88   content::RecordAction(
     89       base::UserMetricsAction("AppList_ClickOnAppFromSearch"));
     90 
     91   controller_->ActivateApp(
     92       profile_,
     93       extension,
     94       AppListControllerDelegate::LAUNCH_FROM_APP_LIST_SEARCH,
     95       event_flags);
     96 }
     97 
     98 void AppResult::InvokeAction(int action_index, int event_flags) {}
     99 
    100 scoped_ptr<ChromeSearchResult> AppResult::Duplicate() {
    101   scoped_ptr<ChromeSearchResult> copy(
    102       new AppResult(profile_, app_id_, controller_));
    103   copy->set_title(title());
    104   copy->set_title_tags(title_tags());
    105 
    106   return copy.Pass();
    107 }
    108 
    109 ChromeSearchResultType AppResult::GetType() {
    110   return APP_SEARCH_RESULT;
    111 }
    112 
    113 ui::MenuModel* AppResult::GetContextMenuModel() {
    114   if (!context_menu_) {
    115     context_menu_.reset(new AppContextMenu(
    116         this, profile_, app_id_, controller_));
    117     context_menu_->set_is_platform_app(is_platform_app_);
    118     context_menu_->set_is_search_result(true);
    119   }
    120 
    121   return context_menu_->GetMenuModel();
    122 }
    123 
    124 void AppResult::StartObservingExtensionRegistry() {
    125   DCHECK(!extension_registry_);
    126 
    127   extension_registry_ = extensions::ExtensionRegistry::Get(profile_);
    128   extension_registry_->AddObserver(this);
    129 }
    130 
    131 void AppResult::StopObservingExtensionRegistry() {
    132   if (extension_registry_)
    133     extension_registry_->RemoveObserver(this);
    134   extension_registry_ = NULL;
    135 }
    136 
    137 bool AppResult::RunExtensionEnableFlow() {
    138   if (extensions::util::IsAppLaunchableWithoutEnabling(app_id_, profile_))
    139     return false;
    140 
    141   if (!extension_enable_flow_) {
    142     controller_->OnShowChildDialog();
    143 
    144     extension_enable_flow_.reset(new ExtensionEnableFlow(
    145         profile_, app_id_, this));
    146     extension_enable_flow_->StartForNativeWindow(
    147         controller_->GetAppListWindow());
    148   }
    149   return true;
    150 }
    151 
    152 void AppResult::UpdateIcon() {
    153   gfx::ImageSkia icon = icon_->image_skia();
    154 
    155   if (!extensions::util::IsAppLaunchable(app_id_, profile_)) {
    156     const color_utils::HSL shift = {-1, 0, 0.6};
    157     icon = gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon, shift);
    158   }
    159 
    160   SetIcon(icon);
    161 }
    162 
    163 void AppResult::OnExtensionIconImageChanged(extensions::IconImage* image) {
    164   DCHECK_EQ(icon_.get(), image);
    165   UpdateIcon();
    166 }
    167 
    168 void AppResult::ExecuteLaunchCommand(int event_flags) {
    169   Open(event_flags);
    170 }
    171 
    172 void AppResult::ExtensionEnableFlowFinished() {
    173   extension_enable_flow_.reset();
    174   controller_->OnCloseChildDialog();
    175 
    176   // Automatically open app after enabling.
    177   Open(ui::EF_NONE);
    178 }
    179 
    180 void AppResult::ExtensionEnableFlowAborted(bool user_initiated) {
    181   extension_enable_flow_.reset();
    182   controller_->OnCloseChildDialog();
    183 }
    184 
    185 void AppResult::OnExtensionLoaded(content::BrowserContext* browser_context,
    186                                   const extensions::Extension* extension) {
    187   UpdateIcon();
    188 }
    189 
    190 void AppResult::OnExtensionUninstalled(content::BrowserContext* browser_context,
    191                                        const extensions::Extension* extension) {
    192   if (extension->id() != app_id_)
    193     return;
    194 
    195   NotifyItemUninstalled();
    196 }
    197 
    198 void AppResult::OnShutdown(extensions::ExtensionRegistry* registry) {
    199   DCHECK_EQ(extension_registry_, registry);
    200   StopObservingExtensionRegistry();
    201 }
    202 
    203 }  // namespace app_list
    204