190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// found in the LICENSE file.
490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/app_search_provider.h"
690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <string>
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/extensions/extension_ui_util.h"
128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
1390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
1490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/app_result.h"
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/extension_prefs.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension_set.h"
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/app_list/search/tokenized_string.h"
211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/app_list/search/tokenized_string_match.h"
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using extensions::ExtensionRegistry;
2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace app_list {
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)class AppSearchProvider::App {
2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) public:
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  explicit App(const extensions::Extension* extension,
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               const base::Time& last_launch_time)
3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      : app_id_(extension->id()),
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        indexed_name_(base::UTF8ToUTF16(extension->name())),
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        last_launch_time_(last_launch_time) {}
3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  ~App() {}
3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const std::string& app_id() const { return app_id_; }
3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const TokenizedString& indexed_name() const { return indexed_name_; }
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const base::Time& last_launch_time() const { return last_launch_time_; }
3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) private:
4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const std::string app_id_;
4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  TokenizedString indexed_name_;
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  base::Time last_launch_time_;
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(App);
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)};
4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)AppSearchProvider::AppSearchProvider(Profile* profile,
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                     AppListControllerDelegate* list_controller)
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    : profile_(profile),
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      list_controller_(list_controller),
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      extension_registry_observer_(this) {
53f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  RefreshApps();
5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AppSearchProvider::~AppSearchProvider() {}
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void AppSearchProvider::Start(const base::string16& query) {
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  StartImpl(base::Time::Now(), query);
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void AppSearchProvider::Stop() {
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void AppSearchProvider::StartImpl(const base::Time& current_time,
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  const base::string16& query) {
6890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const TokenizedString query_terms(query);
6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
7090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  ClearResults();
7190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  bool show_recommendations = query.empty();
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Refresh list of apps to ensure we have the latest launch time information.
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (show_recommendations)
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    RefreshApps();
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for (Apps::const_iterator app_it = apps_.begin();
7890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       app_it != apps_.end();
7990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       ++app_it) {
8090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    scoped_ptr<AppResult> result(
8190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        new AppResult(profile_, (*app_it)->app_id(), list_controller_));
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (show_recommendations) {
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      result->set_title((*app_it)->indexed_name().text());
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      result->UpdateFromLastLaunched(current_time,
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                     (*app_it)->last_launch_time());
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    } else {
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      TokenizedStringMatch match;
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (!match.Calculate(query_terms, (*app_it)->indexed_name()))
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        continue;
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      result->UpdateFromMatch((*app_it)->indexed_name(), match);
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
93f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    Add(result.PassAs<SearchResult>());
9490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
9590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
9690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppSearchProvider::AddApps(const extensions::ExtensionSet& extensions) {
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != extensions.end(); ++iter) {
1017d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const extensions::Extension* app = iter->get();
1023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (!extensions::ui_util::ShouldDisplayInAppLauncher(app, profile_))
10490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      continue;
10590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (profile_->IsOffTheRecord() &&
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        !extensions::util::CanLoadInIncognito(app, profile_))
10890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      continue;
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    apps_.push_back(new App(app, prefs->GetLastLaunchTime(app->id())));
11190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
11290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
11390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)void AppSearchProvider::RefreshApps() {
1153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  apps_.clear();
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AddApps(registry->enabled_extensions());
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AddApps(registry->disabled_extensions());
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AddApps(registry->terminated_extensions());
1203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
1213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void AppSearchProvider::OnExtensionLoaded(
123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    content::BrowserContext* browser_context,
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const extensions::Extension* extension) {
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  RefreshApps();
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void AppSearchProvider::OnExtensionUninstalled(
129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    content::BrowserContext* browser_context,
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const extensions::Extension* extension,
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    extensions::UninstallReason reason) {
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  RefreshApps();
13390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
13490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
13590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}  // namespace app_list
136