summaryrefslogtreecommitdiffstats
path: root/chrome/browser/apps
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-19 04:10:13 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-19 04:10:13 +0000
commitc30bda26c225a049e105db1c1164833032c0682f (patch)
treec50de680ba0da6415d9710ef90dbd8fef3f98501 /chrome/browser/apps
parent494eb70be9ba547f417387fdde551c2ca785904f (diff)
downloadchromium_src-c30bda26c225a049e105db1c1164833032c0682f.zip
chromium_src-c30bda26c225a049e105db1c1164833032c0682f.tar.gz
chromium_src-c30bda26c225a049e105db1c1164833032c0682f.tar.bz2
app_list: Drive app integration.
- DriveAppProvider to map Drive apps to chrome apps or local url apps; - DriveAppMapping to track the mapped chrome apps; - DriveServiceBridge to wrap DriveAPIService and DriveAppRegistry to provide the user Drive apps info; - Put feature behind "--enable-drive-apps-in-app-list"; BUG=358791,345066 TEST=DriveAppProviderTest.*:DriveAppMappingTest.* Review URL: https://codereview.chromium.org/308003005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278265 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/apps')
-rw-r--r--chrome/browser/apps/drive/OWNERS8
-rw-r--r--chrome/browser/apps/drive/drive_app_converter.cc213
-rw-r--r--chrome/browser/apps/drive/drive_app_converter.h74
-rw-r--r--chrome/browser/apps/drive/drive_app_converter_browsertest.cc159
-rw-r--r--chrome/browser/apps/drive/drive_app_mapping.cc127
-rw-r--r--chrome/browser/apps/drive/drive_app_mapping.h47
-rw-r--r--chrome/browser/apps/drive/drive_app_mapping_unittest.cc116
-rw-r--r--chrome/browser/apps/drive/drive_app_provider.cc263
-rw-r--r--chrome/browser/apps/drive/drive_app_provider.h96
-rw-r--r--chrome/browser/apps/drive/drive_app_provider_browsertest.cc453
-rw-r--r--chrome/browser/apps/drive/drive_service_bridge.cc138
-rw-r--r--chrome/browser/apps/drive/drive_service_bridge.h35
12 files changed, 1729 insertions, 0 deletions
diff --git a/chrome/browser/apps/drive/OWNERS b/chrome/browser/apps/drive/OWNERS
new file mode 100644
index 0000000..061c003
--- /dev/null
+++ b/chrome/browser/apps/drive/OWNERS
@@ -0,0 +1,8 @@
+benwells@chromium.org
+calamity@chromium.org
+stevenjb@chromium.org
+tapted@chromium.org
+xiyuan@chromium.org
+
+# Drive API
+kinaba@chromium.org
diff --git a/chrome/browser/apps/drive/drive_app_converter.cc b/chrome/browser/apps/drive/drive_app_converter.cc
new file mode 100644
index 0000000..592622d
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_converter.cc
@@ -0,0 +1,213 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_app_converter.h"
+
+#include <algorithm>
+#include <set>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/install_tracker.h"
+#include "chrome/browser/image_decoder.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/constants.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_status.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+using content::BrowserThread;
+
+// IconFetcher downloads |icon_url| using |converter|'s profile. The icon
+// url is passed from a DriveAppInfo and should follow icon url definition
+// in Drive API:
+// https://developers.google.com/drive/v2/reference/apps#resource
+// Each icon url represents a single image associated with a certain size.
+class DriveAppConverter::IconFetcher : public net::URLFetcherDelegate,
+ public ImageDecoder::Delegate {
+ public:
+ IconFetcher(DriveAppConverter* converter,
+ const GURL& icon_url,
+ int expected_size)
+ : converter_(converter),
+ icon_url_(icon_url),
+ expected_size_(expected_size) {}
+ virtual ~IconFetcher() {
+ if (image_decoder_.get())
+ image_decoder_->set_delegate(NULL);
+ }
+
+ void Start() {
+ fetcher_.reset(
+ net::URLFetcher::Create(icon_url_, net::URLFetcher::GET, this));
+ fetcher_->SetRequestContext(converter_->profile_->GetRequestContext());
+ fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
+ fetcher_->Start();
+ }
+
+ const GURL& icon_url() const { return icon_url_; }
+ const SkBitmap& icon() const { return icon_; }
+
+ private:
+ // net::URLFetcherDelegate overrides:
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
+ CHECK_EQ(fetcher_.get(), source);
+ scoped_ptr<net::URLFetcher> fetcher(fetcher_.Pass());
+
+ if (!fetcher->GetStatus().is_success() ||
+ fetcher->GetResponseCode() != net::HTTP_OK) {
+ converter_->OnIconFetchComplete(this);
+ return;
+ }
+
+ std::string unsafe_icon_data;
+ fetcher->GetResponseAsString(&unsafe_icon_data);
+
+ image_decoder_ =
+ new ImageDecoder(this, unsafe_icon_data, ImageDecoder::DEFAULT_CODEC);
+ image_decoder_->Start(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
+ }
+
+ // ImageDecoder::Delegate overrides:
+ virtual void OnImageDecoded(const ImageDecoder* decoder,
+ const SkBitmap& decoded_image) OVERRIDE {
+ if (decoded_image.width() == expected_size_)
+ icon_ = decoded_image;
+ converter_->OnIconFetchComplete(this);
+ }
+
+ virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE {
+ converter_->OnIconFetchComplete(this);
+ }
+
+ DriveAppConverter* converter_;
+ const GURL icon_url_;
+ const int expected_size_;
+
+ scoped_ptr<net::URLFetcher> fetcher_;
+ scoped_refptr<ImageDecoder> image_decoder_;
+ SkBitmap icon_;
+
+ DISALLOW_COPY_AND_ASSIGN(IconFetcher);
+};
+
+DriveAppConverter::DriveAppConverter(Profile* profile,
+ const drive::DriveAppInfo& drive_app_info,
+ const FinishedCallback& finished_callback)
+ : profile_(profile),
+ drive_app_info_(drive_app_info),
+ extension_(NULL),
+ is_new_install_(false),
+ finished_callback_(finished_callback) {
+ DCHECK(profile_);
+}
+
+DriveAppConverter::~DriveAppConverter() {
+ PostInstallCleanUp();
+}
+
+void DriveAppConverter::Start() {
+ DCHECK(!IsStarted());
+
+ if (drive_app_info_.app_name.empty() ||
+ !drive_app_info_.create_url.is_valid()) {
+ finished_callback_.Run(this, false);
+ return;
+ }
+
+ web_app_.title = base::UTF8ToUTF16(drive_app_info_.app_name);
+ web_app_.app_url = drive_app_info_.create_url;
+
+ const std::set<int> allowed_sizes(extension_misc::kExtensionIconSizes,
+ extension_misc::kExtensionIconSizes +
+ extension_misc::kNumExtensionIconSizes);
+ std::set<int> pending_sizes;
+ for (size_t i = 0; i < drive_app_info_.app_icons.size(); ++i) {
+ const int icon_size = drive_app_info_.app_icons[i].first;
+ if (allowed_sizes.find(icon_size) == allowed_sizes.end() ||
+ pending_sizes.find(icon_size) != pending_sizes.end()) {
+ continue;
+ }
+
+ pending_sizes.insert(icon_size);
+ const GURL& icon_url = drive_app_info_.app_icons[i].second;
+ IconFetcher* fetcher = new IconFetcher(this, icon_url, icon_size);
+ fetchers_.push_back(fetcher); // Pass ownership to |fetchers|.
+ fetcher->Start();
+ }
+
+ if (fetchers_.empty())
+ StartInstall();
+}
+
+bool DriveAppConverter::IsStarted() const {
+ return !fetchers_.empty() || crx_installer_;
+}
+
+bool DriveAppConverter::IsInstalling(const std::string& app_id) const {
+ return crx_installer_ && crx_installer_->extension() &&
+ crx_installer_->extension()->id() == app_id;
+}
+
+void DriveAppConverter::OnIconFetchComplete(const IconFetcher* fetcher) {
+ const SkBitmap& icon = fetcher->icon();
+ if (!icon.empty() && icon.width() != 0) {
+ WebApplicationInfo::IconInfo icon_info;
+ icon_info.url = fetcher->icon_url();
+ icon_info.data = icon;
+ icon_info.width = icon.width();
+ icon_info.height = icon.height();
+ web_app_.icons.push_back(icon_info);
+ }
+
+ fetchers_.erase(std::find(fetchers_.begin(), fetchers_.end(), fetcher));
+
+ if (fetchers_.empty())
+ StartInstall();
+}
+
+void DriveAppConverter::StartInstall() {
+ DCHECK(!crx_installer_);
+ crx_installer_ = extensions::CrxInstaller::CreateSilent(
+ extensions::ExtensionSystem::Get(profile_)->extension_service());
+ // The converted url app should not be syncable. Drive apps go with the user's
+ // account and url apps will be created when needed. Syncing those apps could
+ // hit an edge case where the synced url apps become orphans when the user has
+ // corresponding chrome apps.
+ crx_installer_->set_do_not_sync(true);
+
+ extensions::InstallTracker::Get(profile_)->AddObserver(this);
+ crx_installer_->InstallWebApp(web_app_);
+}
+
+void DriveAppConverter::PostInstallCleanUp() {
+ if (!crx_installer_)
+ return;
+
+ extensions::InstallTracker::Get(profile_)->RemoveObserver(this);
+ crx_installer_ = NULL;
+}
+
+void DriveAppConverter::OnFinishCrxInstall(const std::string& extension_id,
+ bool success) {
+ if (!crx_installer_->extension() ||
+ crx_installer_->extension()->id() != extension_id) {
+ return;
+ }
+
+ extension_ = crx_installer_->extension();
+ is_new_install_ = success && crx_installer_->current_version().empty();
+ PostInstallCleanUp();
+
+ finished_callback_.Run(this, success);
+ // |finished_callback_| could delete this.
+}
diff --git a/chrome/browser/apps/drive/drive_app_converter.h b/chrome/browser/apps/drive/drive_app_converter.h
new file mode 100644
index 0000000..772ea1e
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_converter.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_CONVERTER_H_
+#define CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_CONVERTER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/drive/drive_app_registry.h"
+#include "chrome/browser/extensions/install_observer.h"
+#include "chrome/common/web_application_info.h"
+
+class Profile;
+
+namespace extensions {
+class CrxInstaller;
+class Extension;
+}
+
+// DriveAppConverter creates and installs a local URL app for the given
+// DriveAppInfo into the given profile.
+class DriveAppConverter : public extensions::InstallObserver {
+ public:
+ typedef base::Callback<void(const DriveAppConverter*, bool success)>
+ FinishedCallback;
+
+ DriveAppConverter(Profile* profile,
+ const drive::DriveAppInfo& drive_app_info,
+ const FinishedCallback& finished_callback);
+ virtual ~DriveAppConverter();
+
+ void Start();
+ bool IsStarted() const;
+
+ bool IsInstalling(const std::string& app_id) const;
+
+ const drive::DriveAppInfo& drive_app_info() const { return drive_app_info_; }
+ const extensions::Extension* extension() const { return extension_; }
+ bool is_new_install() const { return is_new_install_; }
+
+ private:
+ class IconFetcher;
+
+ // Callbacks from IconFetcher.
+ void OnIconFetchComplete(const IconFetcher* fetcher);
+
+ void StartInstall();
+ void PostInstallCleanUp();
+
+ // extensions::InstallObserver:
+ virtual void OnFinishCrxInstall(const std::string& extension_id,
+ bool success) OVERRIDE;
+
+ Profile* profile_;
+ const drive::DriveAppInfo drive_app_info_;
+
+ WebApplicationInfo web_app_;
+ const extensions::Extension* extension_;
+ bool is_new_install_;
+
+ ScopedVector<IconFetcher> fetchers_;
+ scoped_refptr<extensions::CrxInstaller> crx_installer_;
+
+ FinishedCallback finished_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppConverter);
+};
+
+#endif // CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_CONVERTER_H_
diff --git a/chrome/browser/apps/drive/drive_app_converter_browsertest.cc b/chrome/browser/apps/drive/drive_app_converter_browsertest.cc
new file mode 100644
index 0000000..ce28ba4
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_converter_browsertest.cc
@@ -0,0 +1,159 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_app_converter.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/version.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/icons_handler.h"
+#include "extensions/common/permissions/permission_set.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using extensions::AppLaunchInfo;
+using extensions::Extension;
+using extensions::ExtensionRegistry;
+
+namespace {
+
+const char kAppName[] = "Test drive app";
+const char kAppUrl[] = "http://foobar.com/drive_app";
+
+} // namespace
+
+class DriveAppConverterTest : public ExtensionBrowserTest {
+ public:
+ DriveAppConverterTest() {}
+ virtual ~DriveAppConverterTest() {}
+
+ // ExtensionBrowserTest:
+ virtual void SetUpOnMainThread() OVERRIDE {
+ ExtensionBrowserTest::SetUpOnMainThread();
+
+ base::FilePath test_data_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ }
+
+ void InstallAndWaitFinish(const drive::DriveAppInfo& drive_app) {
+ runner_ = new content::MessageLoopRunner;
+
+ converter_.reset(new DriveAppConverter(
+ profile(),
+ drive_app,
+ base::Bind(&DriveAppConverterTest::ConverterFinished,
+ base::Unretained(this))));
+ converter_->Start();
+
+ runner_->Run();
+ }
+
+ GURL GetTestUrl(const std::string& path) {
+ return embedded_test_server()->base_url().Resolve(path);
+ }
+
+ drive::DriveAppInfo GetTestDriveApp() {
+ // Define four icons. icon1.png is 16x16 and good to use. icon2.png is
+ // 16x16 but claims to be 32x32 and should be dropped. icon3.png is 66x66
+ // and not a valid extension icon size and should be dropped too. The forth
+ // one is icon2.png with 16x16 but should be ignored because 16x16 already
+ // has icon1.png as its resource.
+ drive::DriveAppInfo::IconList app_icons;
+ app_icons.push_back(std::make_pair(16, GetTestUrl("extensions/icon1.png")));
+ app_icons.push_back(std::make_pair(32, GetTestUrl("extensions/icon2.png")));
+ app_icons.push_back(std::make_pair(66, GetTestUrl("extensions/icon3.png")));
+ app_icons.push_back(std::make_pair(16, GetTestUrl("extensions/icon2.png")));
+
+ drive::DriveAppInfo::IconList document_icons;
+
+ return drive::DriveAppInfo("fake_drive_app_id",
+ "fake_product_id",
+ app_icons,
+ document_icons,
+ kAppName,
+ GURL(kAppUrl),
+ true);
+ }
+
+ const DriveAppConverter* converter() const { return converter_.get(); }
+
+ private:
+ void ConverterFinished(const DriveAppConverter* converter, bool success) {
+ if (runner_)
+ runner_->Quit();
+ }
+
+ scoped_ptr<DriveAppConverter> converter_;
+ scoped_refptr<content::MessageLoopRunner> runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppConverterTest);
+};
+
+IN_PROC_BROWSER_TEST_F(DriveAppConverterTest, GoodApp) {
+ InstallAndWaitFinish(GetTestDriveApp());
+
+ const Extension* app = converter()->extension();
+ ASSERT_TRUE(app != NULL);
+ EXPECT_EQ(kAppName, app->name());
+ EXPECT_TRUE(app->is_hosted_app());
+ EXPECT_TRUE(app->from_bookmark());
+ EXPECT_EQ(GURL(kAppUrl), AppLaunchInfo::GetLaunchWebURL(app));
+ EXPECT_EQ(extensions::LAUNCH_CONTAINER_TAB,
+ AppLaunchInfo::GetLaunchContainer(app));
+ EXPECT_EQ(0u, app->permissions_data()->active_permissions()->apis().size());
+ EXPECT_EQ(1u, extensions::IconsInfo::GetIcons(app).map().size());
+
+ const Extension* installed = extensions::ExtensionSystem::Get(profile())
+ ->extension_service()
+ ->GetInstalledExtension(app->id());
+ EXPECT_EQ(app, installed);
+ EXPECT_FALSE(extensions::util::ShouldSyncApp(app, profile()));
+}
+
+IN_PROC_BROWSER_TEST_F(DriveAppConverterTest, BadApp) {
+ drive::DriveAppInfo no_name = GetTestDriveApp();
+ no_name.app_name.clear();
+ InstallAndWaitFinish(no_name);
+ EXPECT_TRUE(converter()->extension() == NULL);
+
+ drive::DriveAppInfo no_url = GetTestDriveApp();
+ no_url.create_url = GURL();
+ InstallAndWaitFinish(no_url);
+ EXPECT_TRUE(converter()->extension() == NULL);
+}
+
+IN_PROC_BROWSER_TEST_F(DriveAppConverterTest, InstallTwice) {
+ InstallAndWaitFinish(GetTestDriveApp());
+ const Extension* first_install = converter()->extension();
+ ASSERT_TRUE(first_install != NULL);
+ EXPECT_TRUE(converter()->is_new_install());
+ const std::string first_install_id = first_install->id();
+ const base::Version first_install_version(first_install->VersionString());
+ ASSERT_TRUE(first_install_version.IsValid());
+
+ InstallAndWaitFinish(GetTestDriveApp());
+ const Extension* second_install = converter()->extension();
+ ASSERT_TRUE(second_install != NULL);
+ EXPECT_FALSE(converter()->is_new_install());
+
+ // Two different app instances.
+ ASSERT_NE(first_install, second_install);
+ EXPECT_EQ(first_install_id, second_install->id());
+ EXPECT_GE(second_install->version()->CompareTo(first_install_version), 0);
+}
diff --git a/chrome/browser/apps/drive/drive_app_mapping.cc b/chrome/browser/apps/drive/drive_app_mapping.cc
new file mode 100644
index 0000000..ae95310
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_mapping.cc
@@ -0,0 +1,127 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_app_mapping.h"
+
+#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+
+namespace {
+
+// Key for a string value that holds the mapped chrome app id.
+const char kKeyChromeApp[] = "chrome_app";
+
+// Key for a boolean value of whether the mapped chrome app is auto generated.
+// Default is false.
+const char kKeyGenerated[] = "generated";
+
+scoped_ptr<base::DictionaryValue> CreateInfoDict(
+ const std::string& chrome_app_id,
+ bool generated) {
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetStringWithoutPathExpansion(kKeyChromeApp, chrome_app_id);
+
+ // Only writes non-default value.
+ if (generated)
+ dict->SetBooleanWithoutPathExpansion(kKeyGenerated, true);
+ return dict.Pass();
+}
+
+} // namespace
+
+DriveAppMapping::DriveAppMapping(PrefService* prefs) : prefs_(prefs) {
+}
+
+DriveAppMapping::~DriveAppMapping() {
+}
+
+// static
+void DriveAppMapping::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterDictionaryPref(
+ prefs::kAppLauncherDriveAppMapping,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+void DriveAppMapping::Add(const std::string& drive_app_id,
+ const std::string& chrome_app_id,
+ bool generated) {
+ DictionaryPrefUpdate update(prefs_, prefs::kAppLauncherDriveAppMapping);
+ update->SetWithoutPathExpansion(
+ drive_app_id, CreateInfoDict(chrome_app_id, generated).release());
+}
+
+void DriveAppMapping::Remove(const std::string& drive_app_id) {
+ DictionaryPrefUpdate update(prefs_, prefs::kAppLauncherDriveAppMapping);
+ update->RemoveWithoutPathExpansion(drive_app_id, NULL);
+}
+
+std::string DriveAppMapping::GetChromeApp(
+ const std::string& drive_app_id) const {
+ const base::DictionaryValue* mapping =
+ prefs_->GetDictionary(prefs::kAppLauncherDriveAppMapping);
+ const base::DictionaryValue* info_dict;
+ std::string chrome_app_id;
+ if (mapping->GetDictionaryWithoutPathExpansion(drive_app_id, &info_dict) &&
+ info_dict->GetStringWithoutPathExpansion(kKeyChromeApp, &chrome_app_id)) {
+ return chrome_app_id;
+ }
+
+ return std::string();
+}
+
+std::string DriveAppMapping::GetDriveApp(
+ const std::string& chrome_app_id) const {
+ const base::DictionaryValue* mapping =
+ prefs_->GetDictionary(prefs::kAppLauncherDriveAppMapping);
+ for (base::DictionaryValue::Iterator it(*mapping); !it.IsAtEnd();
+ it.Advance()) {
+ const base::DictionaryValue* info_dict;
+ std::string value_string;
+ DCHECK(it.value().IsType(base::Value::TYPE_DICTIONARY));
+ if (it.value().GetAsDictionary(&info_dict) &&
+ info_dict->GetStringWithoutPathExpansion(kKeyChromeApp,
+ &value_string) &&
+ value_string == chrome_app_id) {
+ return it.key();
+ }
+ }
+ return std::string();
+}
+
+bool DriveAppMapping::IsChromeAppGenerated(
+ const std::string& chrome_app_id) const {
+ const base::DictionaryValue* mapping =
+ prefs_->GetDictionary(prefs::kAppLauncherDriveAppMapping);
+ for (base::DictionaryValue::Iterator it(*mapping); !it.IsAtEnd();
+ it.Advance()) {
+ const base::DictionaryValue* info_dict;
+ std::string value_string;
+ bool generated;
+ DCHECK(it.value().IsType(base::Value::TYPE_DICTIONARY));
+ if (it.value().GetAsDictionary(&info_dict) &&
+ info_dict->GetStringWithoutPathExpansion(kKeyChromeApp,
+ &value_string) &&
+ value_string == chrome_app_id &&
+ info_dict->GetBooleanWithoutPathExpansion(kKeyGenerated, &generated)) {
+ return generated;
+ }
+ }
+
+ return false;
+}
+
+std::set<std::string> DriveAppMapping::GetDriveAppIds() const {
+ const base::DictionaryValue* mapping =
+ prefs_->GetDictionary(prefs::kAppLauncherDriveAppMapping);
+ std::set<std::string> keys;
+ for (base::DictionaryValue::Iterator it(*mapping); !it.IsAtEnd();
+ it.Advance()) {
+ keys.insert(it.key());
+ }
+ return keys;
+}
diff --git a/chrome/browser/apps/drive/drive_app_mapping.h b/chrome/browser/apps/drive/drive_app_mapping.h
new file mode 100644
index 0000000..be3c1ee
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_mapping.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_MAPPING_H_
+#define CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_MAPPING_H_
+
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class PrefService;
+
+// DriveAppMapping tracks the mapping between Drive apps and corresponding
+// Chrome apps. The data is backed by kAppLauncherDriveAppMapping pref.
+class DriveAppMapping {
+ public:
+ explicit DriveAppMapping(PrefService* prefs);
+ ~DriveAppMapping();
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Adds a mapping from |drive_app_id| to |chrome_app_id|. |generated|
+ // represents whether the corresponding Chrome app is generated.
+ void Add(const std::string& drive_app_id,
+ const std::string& chrome_app_id,
+ bool generated);
+ void Remove(const std::string& drive_app_id);
+
+ std::string GetChromeApp(const std::string& drive_app_id) const;
+ std::string GetDriveApp(const std::string& chrome_app_id) const;
+ bool IsChromeAppGenerated(const std::string& chrome_app_id) const;
+
+ std::set<std::string> GetDriveAppIds() const;
+
+ private:
+ PrefService* prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppMapping);
+};
+
+#endif // CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_MAPPING_H_
diff --git a/chrome/browser/apps/drive/drive_app_mapping_unittest.cc b/chrome/browser/apps/drive/drive_app_mapping_unittest.cc
new file mode 100644
index 0000000..a1f8949
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_mapping_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_app_mapping.h"
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/test/base/testing_pref_service_syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class DriveAppMappingTest : public testing::Test {
+ public:
+ DriveAppMappingTest() {}
+ virtual ~DriveAppMappingTest() {}
+
+ // testing::Test:
+ virtual void SetUp() OVERRIDE {
+ pref_service_.reset(new TestingPrefServiceSyncable);
+ DriveAppMapping::RegisterProfilePrefs(pref_service_->registry());
+
+ mapping_.reset(new DriveAppMapping(pref_service_.get()));
+ }
+
+ DriveAppMapping* mapping() { return mapping_.get(); }
+
+ private:
+ scoped_ptr<TestingPrefServiceSyncable> pref_service_;
+ scoped_ptr<DriveAppMapping> mapping_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppMappingTest);
+};
+
+TEST_F(DriveAppMappingTest, Empty) {
+ EXPECT_EQ("", mapping()->GetChromeApp(""));
+ EXPECT_EQ("", mapping()->GetDriveApp(""));
+ EXPECT_EQ("", mapping()->GetChromeApp("non-existent-drive-app"));
+ EXPECT_EQ("", mapping()->GetDriveApp("non-existent-chrome-app"));
+ EXPECT_EQ(0u, mapping()->GetDriveAppIds().size());
+}
+
+TEST_F(DriveAppMappingTest, Add) {
+ std::set<std::string> drive_app_ids;
+
+ // Add one.
+ mapping()->Add("drive", "chrome", false);
+ EXPECT_EQ("chrome", mapping()->GetChromeApp("drive"));
+ EXPECT_EQ("drive", mapping()->GetDriveApp("chrome"));
+ EXPECT_FALSE(mapping()->IsChromeAppGenerated("chrome"));
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(1u, drive_app_ids.size());
+ EXPECT_EQ(1u, drive_app_ids.count("drive"));
+
+ // Overwrite previous mapping if added under the same key.
+ mapping()->Add("drive", "another-chrome-app", true);
+ EXPECT_EQ("", mapping()->GetDriveApp("chrome"));
+ EXPECT_FALSE(mapping()->IsChromeAppGenerated("chrome"));
+ EXPECT_EQ("another-chrome-app", mapping()->GetChromeApp("drive"));
+ EXPECT_EQ("drive", mapping()->GetDriveApp("another-chrome-app"));
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated("another-chrome-app"));
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(1u, drive_app_ids.size());
+ EXPECT_EQ(1u, drive_app_ids.count("drive"));
+
+ // Add another one.
+ mapping()->Add("drive-1", "chrome-1", false);
+ EXPECT_EQ("chrome-1", mapping()->GetChromeApp("drive-1"));
+ EXPECT_EQ("drive-1", mapping()->GetDriveApp("chrome-1"));
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(2u, drive_app_ids.size());
+ EXPECT_EQ(1u, drive_app_ids.count("drive"));
+ EXPECT_EQ(1u, drive_app_ids.count("drive-1"));
+
+ // Previous mapping should not be affected by new mapping.
+ EXPECT_EQ("another-chrome-app", mapping()->GetChromeApp("drive"));
+ EXPECT_EQ("drive", mapping()->GetDriveApp("another-chrome-app"));
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated("another-chrome-app"));
+
+ // Non-existent value returns empty string.
+ EXPECT_EQ("", mapping()->GetChromeApp("non-existent-drive-app"));
+ EXPECT_EQ("", mapping()->GetDriveApp("non-existent-chrome-app"));
+}
+
+TEST_F(DriveAppMappingTest, Remove) {
+ std::set<std::string> drive_app_ids;
+
+ // Prepare data.
+ mapping()->Add("drive-1", "chrome-1", false);
+ mapping()->Add("drive-2", "chrome-2", false);
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(2u, drive_app_ids.size());
+
+ // Remove non-existent.
+ mapping()->Remove("non-existent-drive-app");
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(2u, drive_app_ids.size());
+
+ // Remove one.
+ EXPECT_EQ("chrome-1", mapping()->GetChromeApp("drive-1"));
+ mapping()->Remove("drive-1");
+ EXPECT_EQ("", mapping()->GetChromeApp("drive-1"));
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(1u, drive_app_ids.size());
+
+ // Remove it again has no effect.
+ mapping()->Remove("drive-1");
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(1u, drive_app_ids.size());
+
+ // Remove another one.
+ EXPECT_EQ("chrome-2", mapping()->GetChromeApp("drive-2"));
+ mapping()->Remove("drive-2");
+ EXPECT_EQ("", mapping()->GetChromeApp("drive-2"));
+ drive_app_ids = mapping()->GetDriveAppIds();
+ EXPECT_EQ(0u, drive_app_ids.size());
+}
diff --git a/chrome/browser/apps/drive/drive_app_provider.cc b/chrome/browser/apps/drive/drive_app_provider.cc
new file mode 100644
index 0000000..2bde9fe
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_provider.cc
@@ -0,0 +1,263 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_app_provider.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "chrome/browser/apps/drive/drive_app_converter.h"
+#include "chrome/browser/apps/drive/drive_app_mapping.h"
+#include "chrome/browser/apps/drive/drive_service_bridge.h"
+#include "chrome/browser/drive/drive_app_registry.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_factory.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+
+using extensions::Extension;
+using extensions::ExtensionRegistry;
+
+namespace {
+
+void IgnoreUninstallResult(google_apis::GDataErrorCode) {
+}
+
+} // namespace
+
+DriveAppProvider::DriveAppProvider(Profile* profile)
+ : profile_(profile),
+ service_bridge_(DriveServiceBridge::Create(profile).Pass()),
+ mapping_(new DriveAppMapping(profile->GetPrefs())),
+ weak_ptr_factory_(this) {
+ service_bridge_->GetAppRegistry()->AddObserver(this);
+ ExtensionRegistry::Get(profile_)->AddObserver(this);
+}
+
+DriveAppProvider::~DriveAppProvider() {
+ ExtensionRegistry::Get(profile_)->RemoveObserver(this);
+ service_bridge_->GetAppRegistry()->RemoveObserver(this);
+}
+
+// static
+void DriveAppProvider::AppendDependsOnFactories(
+ std::set<BrowserContextKeyedServiceFactory*>* factories) {
+ factories->insert(extensions::ExtensionRegistryFactory::GetInstance());
+ DriveServiceBridge::AppendDependsOnFactories(factories);
+}
+
+void DriveAppProvider::SetDriveServiceBridgeForTest(
+ scoped_ptr<DriveServiceBridge> test_bridge) {
+ service_bridge_->GetAppRegistry()->RemoveObserver(this);
+ service_bridge_ = test_bridge.Pass();
+ service_bridge_->GetAppRegistry()->AddObserver(this);
+}
+
+void DriveAppProvider::UpdateMappingAndExtensionSystem(
+ const std::string& drive_app_id,
+ const Extension* new_app,
+ bool is_new_app_generated) {
+ const std::string& new_chrome_app_id = new_app->id();
+
+ const std::string existing_chrome_app_id =
+ mapping_->GetChromeApp(drive_app_id);
+ if (existing_chrome_app_id == new_chrome_app_id)
+ return;
+
+ const bool is_existing_app_generated =
+ mapping_->IsChromeAppGenerated(existing_chrome_app_id);
+ mapping_->Add(drive_app_id, new_chrome_app_id, is_new_app_generated);
+
+ const Extension* existing_app =
+ ExtensionRegistry::Get(profile_)->GetExtensionById(
+ existing_chrome_app_id, ExtensionRegistry::EVERYTHING);
+ if (existing_app && is_existing_app_generated) {
+ extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->UninstallExtension(existing_chrome_app_id, false, NULL);
+ }
+}
+
+void DriveAppProvider::ProcessDeferredOnExtensionInstalled(
+ const std::string drive_app_id,
+ const std::string chrome_app_id) {
+ const Extension* app = ExtensionRegistry::Get(profile_)->GetExtensionById(
+ chrome_app_id, ExtensionRegistry::EVERYTHING);
+ if (!app)
+ return;
+
+ UpdateMappingAndExtensionSystem(drive_app_id, app, false);
+}
+
+void DriveAppProvider::SchedulePendingConverters() {
+ if (pending_converters_.empty())
+ return;
+
+ if (!pending_converters_.front()->IsStarted())
+ pending_converters_.front()->Start();
+}
+
+void DriveAppProvider::OnLocalAppConverted(const DriveAppConverter* converter,
+ bool success) {
+ DCHECK_EQ(pending_converters_.front(), converter);
+
+ if (success) {
+ const bool was_generated =
+ mapping_->GetDriveApp(converter->extension()->id()) ==
+ converter->drive_app_info().app_id &&
+ mapping_->IsChromeAppGenerated(converter->extension()->id());
+ UpdateMappingAndExtensionSystem(
+ converter->drive_app_info().app_id,
+ converter->extension(),
+ converter->is_new_install() || was_generated);
+ } else {
+ LOG(WARNING) << "Failed to convert drive app to web app, "
+ << "drive app id= " << converter->drive_app_info().app_id
+ << ", name=" << converter->drive_app_info().app_name;
+ }
+
+ pending_converters_.erase(pending_converters_.begin());
+ SchedulePendingConverters();
+}
+
+bool DriveAppProvider::IsMappedUrlAppUpToDate(
+ const drive::DriveAppInfo& drive_app) const {
+ const std::string& url_app_id = mapping_->GetChromeApp(drive_app.app_id);
+ if (url_app_id.empty())
+ return false;
+
+ const Extension* url_app = ExtensionRegistry::Get(profile_)->GetExtensionById(
+ url_app_id, ExtensionRegistry::EVERYTHING);
+ if (!url_app)
+ return false;
+ DCHECK(url_app->is_hosted_app() && url_app->from_bookmark());
+
+ return drive_app.app_name == url_app->name() &&
+ drive_app.create_url ==
+ extensions::AppLaunchInfo::GetLaunchWebURL(url_app);
+}
+
+void DriveAppProvider::AddOrUpdateDriveApp(
+ const drive::DriveAppInfo& drive_app) {
+ const Extension* chrome_app =
+ ExtensionRegistry::Get(profile_)->GetExtensionById(
+ drive_app.product_id, ExtensionRegistry::EVERYTHING);
+ if (chrome_app) {
+ UpdateMappingAndExtensionSystem(drive_app.app_id, chrome_app, false);
+ return;
+ }
+
+ if (IsMappedUrlAppUpToDate(drive_app))
+ return;
+
+ ScopedVector<DriveAppConverter>::iterator it = pending_converters_.begin();
+ while (it != pending_converters_.end()) {
+ if (!(*it)->IsStarted() &&
+ (*it)->drive_app_info().app_id == drive_app.app_id) {
+ it = pending_converters_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ pending_converters_.push_back(
+ new DriveAppConverter(profile_,
+ drive_app,
+ base::Bind(&DriveAppProvider::OnLocalAppConverted,
+ base::Unretained(this))));
+}
+
+void DriveAppProvider::ProcessRemovedDriveApp(const std::string& drive_app_id) {
+ const std::string chrome_app_id = mapping_->GetChromeApp(drive_app_id);
+ const bool is_generated = mapping_->IsChromeAppGenerated(chrome_app_id);
+ mapping_->Remove(drive_app_id);
+
+ if (chrome_app_id.empty() || !is_generated)
+ return;
+
+ const Extension* existing_app =
+ ExtensionRegistry::Get(profile_)
+ ->GetExtensionById(chrome_app_id, ExtensionRegistry::EVERYTHING);
+ if (!existing_app)
+ return;
+
+ extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->UninstallExtension(chrome_app_id, false, NULL);
+}
+
+void DriveAppProvider::OnDriveAppRegistryUpdated() {
+ service_bridge_->GetAppRegistry()->GetAppList(&drive_apps_);
+
+ IdSet current_ids;
+ for (size_t i = 0; i < drive_apps_.size(); ++i)
+ current_ids.insert(drive_apps_[i].app_id);
+
+ const IdSet existing_ids = mapping_->GetDriveAppIds();
+ const IdSet ids_to_remove =
+ base::STLSetDifference<IdSet>(existing_ids, current_ids);
+ for (IdSet::const_iterator it = ids_to_remove.begin();
+ it != ids_to_remove.end();
+ ++it) {
+ ProcessRemovedDriveApp(*it);
+ }
+
+ for (size_t i = 0; i < drive_apps_.size(); ++i) {
+ AddOrUpdateDriveApp(drive_apps_[i]);
+ }
+ SchedulePendingConverters();
+}
+
+void DriveAppProvider::OnExtensionInstalled(
+ content::BrowserContext* browser_context,
+ const Extension* extension) {
+ // Bail if the |extension| is installed from a converter. The post install
+ // processing will be handled in OnLocalAppConverted.
+ if (!pending_converters_.empty() &&
+ pending_converters_.front()->IsInstalling(extension->id())) {
+ return;
+ }
+
+ // Only user installed app reaches here. If it is mapped, make sure it is not
+ // tagged as generated.
+ const std::string drive_app_id = mapping_->GetDriveApp(extension->id());
+ if (!drive_app_id.empty() &&
+ mapping_->IsChromeAppGenerated(extension->id())) {
+ mapping_->Add(drive_app_id, extension->id(), false);
+ return;
+ }
+
+ for (size_t i = 0; i < drive_apps_.size(); ++i) {
+ if (drive_apps_[i].product_id == extension->id()) {
+ // Defer the processing because it touches the extensions system and
+ // it is better to let the current task finish to avoid unexpected
+ // incomplete status.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&DriveAppProvider::ProcessDeferredOnExtensionInstalled,
+ weak_ptr_factory_.GetWeakPtr(),
+ drive_apps_[i].app_id,
+ extension->id()));
+ return;
+ }
+ }
+}
+
+void DriveAppProvider::OnExtensionUninstalled(
+ content::BrowserContext* browser_context,
+ const Extension* extension) {
+ std::string drive_app_id = mapping_->GetDriveApp(extension->id());
+ if (drive_app_id.empty())
+ return;
+
+ service_bridge_->GetAppRegistry()->UninstallApp(
+ drive_app_id, base::Bind(&IgnoreUninstallResult));
+}
diff --git a/chrome/browser/apps/drive/drive_app_provider.h b/chrome/browser/apps/drive/drive_app_provider.h
new file mode 100644
index 0000000..12a5e21
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_provider.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_PROVIDER_H_
+#define CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_PROVIDER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/drive/drive_app_registry_observer.h"
+#include "extensions/browser/extension_registry_observer.h"
+
+namespace drive {
+struct DriveAppInfo;
+}
+
+class BrowserContextKeyedServiceFactory;
+class DriveAppConverter;
+class DriveAppMapping;
+class DriveServiceBridge;
+class ExtensionService;
+class Profile;
+
+// DriveAppProvider is the integration point for Drive apps. It ensures each
+// Drive app has a corresponding Chrome app in the extension system. If there
+// is no matching Chrome app, a local URL app would be created. The class
+// processes app changes from both DriveAppRegistry and extension system to
+// keep the two in sync.
+class DriveAppProvider : public drive::DriveAppRegistryObserver,
+ public extensions::ExtensionRegistryObserver {
+ public:
+ explicit DriveAppProvider(Profile* profile);
+ virtual ~DriveAppProvider();
+
+ // Appends PKS factories this class depends on.
+ static void AppendDependsOnFactories(
+ std::set<BrowserContextKeyedServiceFactory*>* factories);
+
+ void SetDriveServiceBridgeForTest(scoped_ptr<DriveServiceBridge> test_bridge);
+
+ private:
+ friend class DriveAppProviderTest;
+
+ typedef std::set<std::string> IdSet;
+ typedef std::vector<drive::DriveAppInfo> DriveAppInfos;
+
+ // Maps |drive_app_id| to |new_app|'s id in mapping. Auto uninstall existing
+ // mapped app if it is an URL app.
+ void UpdateMappingAndExtensionSystem(const std::string& drive_app_id,
+ const extensions::Extension* new_app,
+ bool is_new_app_generated);
+
+ // Deferred processing of relevant extension installed message.
+ void ProcessDeferredOnExtensionInstalled(const std::string drive_app_id,
+ const std::string chrome_app_id);
+
+ void SchedulePendingConverters();
+ void OnLocalAppConverted(const DriveAppConverter* converter, bool success);
+
+ bool IsMappedUrlAppUpToDate(const drive::DriveAppInfo& drive_app) const;
+
+ void AddOrUpdateDriveApp(const drive::DriveAppInfo& drive_app);
+ void ProcessRemovedDriveApp(const std::string& drive_app_id);
+
+ // drive::DriveAppRegistryObserver overrides:
+ virtual void OnDriveAppRegistryUpdated() OVERRIDE;
+
+ // extensions::ExtensionRegistryObserver overrides:
+ virtual void OnExtensionInstalled(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension) OVERRIDE;
+ virtual void OnExtensionUninstalled(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension) OVERRIDE;
+
+ Profile* profile_;
+
+ scoped_ptr<DriveServiceBridge> service_bridge_;
+ scoped_ptr<DriveAppMapping> mapping_;
+ DriveAppInfos drive_apps_;
+
+ // Tracks the pending web app convertions.
+ ScopedVector<DriveAppConverter> pending_converters_;
+
+ base::WeakPtrFactory<DriveAppProvider> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppProvider);
+};
+
+#endif // CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_PROVIDER_H_
diff --git a/chrome/browser/apps/drive/drive_app_provider_browsertest.cc b/chrome/browser/apps/drive/drive_app_provider_browsertest.cc
new file mode 100644
index 0000000..4165a19
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_app_provider_browsertest.cc
@@ -0,0 +1,453 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_app_provider.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/apps/drive/drive_app_mapping.h"
+#include "chrome/browser/apps/drive/drive_service_bridge.h"
+#include "chrome/browser/drive/drive_app_registry.h"
+#include "chrome/browser/drive/fake_drive_service.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/install_tracker.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/web_application_info.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+
+using extensions::AppLaunchInfo;
+using extensions::Extension;
+using extensions::ExtensionRegistry;
+
+namespace {
+
+const char kDriveAppId[] = "drive_app_id";
+const char kDriveAppName[] = "Fake Drive App";
+const char kLaunchUrl[] = "http://example.com/drive";
+
+// App id of hosted_app.crx.
+const char kChromeAppId[] = "kbmnembihfiondgfjekmnmcbddelicoi";
+
+// Stub drive service bridge.
+class TestDriveServiceBridge : public DriveServiceBridge {
+ public:
+ explicit TestDriveServiceBridge(drive::DriveAppRegistry* registry)
+ : registry_(registry) {}
+ virtual ~TestDriveServiceBridge() {}
+
+ virtual drive::DriveAppRegistry* GetAppRegistry() OVERRIDE {
+ return registry_;
+ }
+
+ private:
+ drive::DriveAppRegistry* registry_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDriveServiceBridge);
+};
+
+} // namespace
+
+class DriveAppProviderTest : public ExtensionBrowserTest,
+ public extensions::InstallObserver {
+ public:
+ DriveAppProviderTest() {}
+ virtual ~DriveAppProviderTest() {}
+
+ // ExtensionBrowserTest:
+ virtual void SetUpOnMainThread() OVERRIDE {
+ ExtensionBrowserTest::SetUpOnMainThread();
+
+ fake_drive_service_.reset(new drive::FakeDriveService);
+ fake_drive_service_->LoadAppListForDriveApi("drive/applist_empty.json");
+ apps_registry_.reset(
+ new drive::DriveAppRegistry(fake_drive_service_.get()));
+
+ provider_.reset(new DriveAppProvider(profile()));
+ provider_->SetDriveServiceBridgeForTest(
+ make_scoped_ptr(new TestDriveServiceBridge(apps_registry_.get()))
+ .PassAs<DriveServiceBridge>());
+ }
+
+ virtual void CleanUpOnMainThread() OVERRIDE {
+ provider_.reset();
+ apps_registry_.reset();
+ fake_drive_service_.reset();
+
+ ExtensionBrowserTest::CleanUpOnMainThread();
+ }
+
+ const Extension* InstallChromeApp(int expected_change) {
+ base::FilePath test_data_path;
+ if (!PathService::Get(chrome::DIR_TEST_DATA, &test_data_path)) {
+ ADD_FAILURE();
+ return NULL;
+ }
+ test_data_path =
+ test_data_path.AppendASCII("extensions").AppendASCII("hosted_app.crx");
+ const Extension* extension =
+ InstallExtension(test_data_path, expected_change);
+ return extension;
+ }
+
+ void RefreshDriveAppRegistry() {
+ apps_registry_->Update();
+ content::RunAllPendingInMessageLoop();
+ }
+
+ void WaitForPendingDriveAppConverters() {
+ DCHECK(!runner_);
+
+ if (provider_->pending_converters_.empty())
+ return;
+
+ runner_ = new content::MessageLoopRunner;
+
+ pending_drive_app_converter_check_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(50),
+ base::Bind(&DriveAppProviderTest::OnPendingDriveAppConverterCheckTimer,
+ base::Unretained(this)));
+
+ runner_->Run();
+
+ pending_drive_app_converter_check_timer_.Stop();
+ runner_ = NULL;
+ }
+
+ void InstallUserUrlApp(const std::string& url) {
+ DCHECK(!runner_);
+ runner_ = new content::MessageLoopRunner;
+
+ WebApplicationInfo web_app;
+ web_app.title = base::ASCIIToUTF16("User installed Url app");
+ web_app.app_url = GURL(url);
+
+ scoped_refptr<extensions::CrxInstaller> crx_installer =
+ extensions::CrxInstaller::CreateSilent(
+ extensions::ExtensionSystem::Get(profile())->extension_service());
+ crx_installer->set_creation_flags(Extension::FROM_BOOKMARK);
+ extensions::InstallTracker::Get(profile())->AddObserver(this);
+ crx_installer->InstallWebApp(web_app);
+
+ runner_->Run();
+ runner_ = NULL;
+ extensions::InstallTracker::Get(profile())->RemoveObserver(this);
+
+ content::RunAllPendingInMessageLoop();
+ }
+
+ bool HasPendingConverters() const {
+ return !provider_->pending_converters_.empty();
+ }
+
+ drive::FakeDriveService* fake_drive_service() {
+ return fake_drive_service_.get();
+ }
+ DriveAppProvider* provider() { return provider_.get(); }
+ DriveAppMapping* mapping() { return provider_->mapping_.get(); }
+
+ private:
+ void OnPendingDriveAppConverterCheckTimer() {
+ if (!HasPendingConverters())
+ runner_->Quit();
+ }
+
+ // extensions::InstallObserver
+ virtual void OnFinishCrxInstall(const std::string& extension_id,
+ bool success) OVERRIDE {
+ runner_->Quit();
+ }
+
+ scoped_ptr<drive::FakeDriveService> fake_drive_service_;
+ scoped_ptr<drive::DriveAppRegistry> apps_registry_;
+ scoped_ptr<DriveAppProvider> provider_;
+
+ base::RepeatingTimer<DriveAppProviderTest>
+ pending_drive_app_converter_check_timer_;
+ scoped_refptr<content::MessageLoopRunner> runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppProviderTest);
+};
+
+// A Drive app maps to an existing Chrome app that has a matching id.
+// Uninstalling the chrome app would also disconnect the drive app.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, ExistingChromeApp) {
+ // Prepare an existing chrome app.
+ const Extension* chrome_app = InstallChromeApp(1);
+ ASSERT_TRUE(chrome_app);
+
+ // Prepare a Drive app that matches the chrome app id.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, chrome_app->id(), kLaunchUrl);
+ RefreshDriveAppRegistry();
+ EXPECT_FALSE(HasPendingConverters());
+
+ // The Drive app should use the matching chrome app.
+ EXPECT_EQ(chrome_app->id(), mapping()->GetChromeApp(kDriveAppId));
+ EXPECT_FALSE(mapping()->IsChromeAppGenerated(chrome_app->id()));
+
+ // Unintalling chrome app should disconnect the Drive app on server.
+ EXPECT_TRUE(fake_drive_service()->HasApp(kDriveAppId));
+ UninstallExtension(chrome_app->id());
+ EXPECT_FALSE(fake_drive_service()->HasApp(kDriveAppId));
+}
+
+// A Drive app creates an URL app when no matching Chrome app presents.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, CreateUrlApp) {
+ // Prepare a Drive app with no underlying chrome app.
+ fake_drive_service()->AddApp(kDriveAppId, kDriveAppName, "", kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // An Url app should be created.
+ const Extension* chrome_app =
+ ExtensionRegistry::Get(profile())->GetExtensionById(
+ mapping()->GetChromeApp(kDriveAppId), ExtensionRegistry::EVERYTHING);
+ ASSERT_TRUE(chrome_app);
+ EXPECT_EQ(kDriveAppName, chrome_app->name());
+ EXPECT_TRUE(chrome_app->is_hosted_app());
+ EXPECT_TRUE(chrome_app->from_bookmark());
+ EXPECT_EQ(GURL(kLaunchUrl), AppLaunchInfo::GetLaunchWebURL(chrome_app));
+
+ EXPECT_EQ(chrome_app->id(), mapping()->GetChromeApp(kDriveAppId));
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(chrome_app->id()));
+
+ // Unintalling the chrome app should disconnect the Drive app on server.
+ EXPECT_TRUE(fake_drive_service()->HasApp(kDriveAppId));
+ UninstallExtension(chrome_app->id());
+ EXPECT_FALSE(fake_drive_service()->HasApp(kDriveAppId));
+}
+
+// A matching Chrome app replaces the created URL app.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, MatchingChromeAppInstalled) {
+ // Prepare a Drive app that matches the not-yet-installed kChromeAppId.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // An Url app should be created.
+ const Extension* url_app =
+ ExtensionRegistry::Get(profile())->GetExtensionById(
+ mapping()->GetChromeApp(kDriveAppId), ExtensionRegistry::EVERYTHING);
+ EXPECT_TRUE(url_app->is_hosted_app());
+ EXPECT_TRUE(url_app->from_bookmark());
+
+ const std::string url_app_id = url_app->id();
+ EXPECT_NE(kChromeAppId, url_app_id);
+ EXPECT_EQ(url_app_id, mapping()->GetChromeApp(kDriveAppId));
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
+
+ // Installs a chrome app with matching id.
+ InstallChromeApp(0);
+
+ // The Drive app should be mapped to chrome app.
+ EXPECT_EQ(kChromeAppId, mapping()->GetChromeApp(kDriveAppId));
+ EXPECT_FALSE(mapping()->IsChromeAppGenerated(kChromeAppId));
+
+ // Url app should be auto uninstalled.
+ EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById(
+ url_app_id, ExtensionRegistry::EVERYTHING));
+}
+
+// Tests that the corresponding URL app is uninstalled when a Drive app is
+// disconnected.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest,
+ DisconnectDriveAppUninstallUrlApp) {
+ // Prepare a Drive app that matches the not-yet-installed kChromeAppId.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // Url app is created.
+ const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
+ url_app_id, ExtensionRegistry::EVERYTHING));
+
+ fake_drive_service()->RemoveAppByProductId(kChromeAppId);
+ RefreshDriveAppRegistry();
+
+ // Url app is auto uninstalled.
+ EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById(
+ url_app_id, ExtensionRegistry::EVERYTHING));
+}
+
+// Tests that the matching Chrome app is preserved when a Drive app is
+// disconnected.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest,
+ DisconnectDriveAppPreserveChromeApp) {
+ // Prepare an existing chrome app.
+ const Extension* chrome_app = InstallChromeApp(1);
+ ASSERT_TRUE(chrome_app);
+
+ // Prepare a Drive app that matches the chrome app id.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ EXPECT_FALSE(HasPendingConverters());
+
+ fake_drive_service()->RemoveAppByProductId(kChromeAppId);
+ RefreshDriveAppRegistry();
+
+ // Chrome app is still present after the Drive app is disconnected.
+ EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
+ kChromeAppId, ExtensionRegistry::EVERYTHING));
+}
+
+// The "generated" flag of an app should stay across Drive app conversion.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, KeepGeneratedFlagBetweenUpdates) {
+ // Prepare a Drive app with no underlying chrome app.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
+
+ // Change name to trigger an update.
+ const char kChangedName[] = "Changed name";
+ fake_drive_service()->RemoveAppByProductId(kChromeAppId);
+ fake_drive_service()->AddApp(
+ kDriveAppId, kChangedName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // It should still map to the same url app id and tagged as generated.
+ EXPECT_EQ(url_app_id, mapping()->GetChromeApp(kDriveAppId));
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
+}
+
+// A new URL app replaces the existing one and keeps existing// position when a
+// Drive app changes its name or URL.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, DriveAppChanged) {
+ // Prepare a Drive app with no underlying chrome app.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // An Url app should be created.
+ const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ const Extension* url_app =
+ ExtensionRegistry::Get(profile())
+ ->GetExtensionById(url_app_id, ExtensionRegistry::EVERYTHING);
+ ASSERT_TRUE(url_app);
+ EXPECT_EQ(kDriveAppName, url_app->name());
+ EXPECT_TRUE(url_app->is_hosted_app());
+ EXPECT_TRUE(url_app->from_bookmark());
+ EXPECT_EQ(GURL(kLaunchUrl), AppLaunchInfo::GetLaunchWebURL(url_app));
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
+
+ // Register the Drive app with a different name and URL.
+ const char kAnotherName[] = "Another drive app name";
+ const char kAnotherLaunchUrl[] = "http://example.com/another_end_point";
+ fake_drive_service()->RemoveAppByProductId(kChromeAppId);
+ fake_drive_service()->AddApp(
+ kDriveAppId, kAnotherName, kChromeAppId, kAnotherLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // Old URL app should be auto uninstalled.
+ url_app = ExtensionRegistry::Get(profile())
+ ->GetExtensionById(url_app_id, ExtensionRegistry::EVERYTHING);
+ EXPECT_FALSE(url_app);
+
+ // New URL app should be used.
+ const std::string new_url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ EXPECT_NE(new_url_app_id, url_app_id);
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(new_url_app_id));
+
+ const Extension* new_url_app =
+ ExtensionRegistry::Get(profile())
+ ->GetExtensionById(new_url_app_id, ExtensionRegistry::EVERYTHING);
+ ASSERT_TRUE(new_url_app);
+ EXPECT_EQ(kAnotherName, new_url_app->name());
+ EXPECT_TRUE(new_url_app->is_hosted_app());
+ EXPECT_TRUE(new_url_app->from_bookmark());
+ EXPECT_EQ(GURL(kAnotherLaunchUrl),
+ AppLaunchInfo::GetLaunchWebURL(new_url_app));
+}
+
+// An existing URL app is not changed when underlying drive app data (name and
+// URL) is not changed.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, NoChange) {
+ // Prepare one Drive app.
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ const Extension* url_app =
+ ExtensionRegistry::Get(profile())
+ ->GetExtensionById(url_app_id, ExtensionRegistry::EVERYTHING);
+
+ // Refresh with no actual change.
+ RefreshDriveAppRegistry();
+ EXPECT_FALSE(HasPendingConverters());
+
+ // Url app should remain unchanged.
+ const std::string new_url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ EXPECT_EQ(new_url_app_id, url_app_id);
+
+ const Extension* new_url_app =
+ ExtensionRegistry::Get(profile())
+ ->GetExtensionById(new_url_app_id, ExtensionRegistry::EVERYTHING);
+ EXPECT_EQ(url_app, new_url_app);
+}
+
+// User installed url app before Drive app conversion should not be tagged
+// as generated and not auto uninstalled.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledBeforeDriveApp) {
+ InstallUserUrlApp(kLaunchUrl);
+
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ EXPECT_FALSE(mapping()->IsChromeAppGenerated(url_app_id));
+
+ fake_drive_service()->RemoveAppByProductId(kChromeAppId);
+ RefreshDriveAppRegistry();
+
+ // Url app is still present after the Drive app is disconnected.
+ EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
+ url_app_id, ExtensionRegistry::EVERYTHING));
+}
+
+// Similar to UserInstalledBeforeDriveApp but test the case where user
+// installation happens after Drive app conversion.
+IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledAfterDriveApp) {
+ fake_drive_service()->AddApp(
+ kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl);
+ RefreshDriveAppRegistry();
+ WaitForPendingDriveAppConverters();
+
+ // Drive app converted and tagged as generated.
+ const std::string url_app_id = mapping()->GetChromeApp(kDriveAppId);
+ EXPECT_TRUE(mapping()->IsChromeAppGenerated(url_app_id));
+
+ // User installation resets the generated flag.
+ InstallUserUrlApp(kLaunchUrl);
+ EXPECT_FALSE(mapping()->IsChromeAppGenerated(url_app_id));
+
+ fake_drive_service()->RemoveAppByProductId(kChromeAppId);
+ RefreshDriveAppRegistry();
+
+ // Url app is still present after the Drive app is disconnected.
+ EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById(
+ url_app_id, ExtensionRegistry::EVERYTHING));
+}
diff --git a/chrome/browser/apps/drive/drive_service_bridge.cc b/chrome/browser/apps/drive/drive_service_bridge.cc
new file mode 100644
index 0000000..207fc35
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_service_bridge.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/drive/drive_service_bridge.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "chrome/browser/drive/drive_api_service.h"
+#include "chrome/browser/drive/drive_app_registry.h"
+#include "chrome/browser/drive/drive_notification_manager.h"
+#include "chrome/browser/drive/drive_notification_manager_factory.h"
+#include "chrome/browser/drive/drive_notification_observer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+// Hosts DriveAPIService and DriveAppRegistry.
+// TODO(xiyuan): Optimize to leverage chromeos::DriveIntegrationService.
+class DriveServiceBridgeImpl : public DriveServiceBridge,
+ public drive::DriveServiceObserver,
+ public drive::DriveNotificationObserver {
+ public:
+ explicit DriveServiceBridgeImpl(Profile* profile);
+ virtual ~DriveServiceBridgeImpl();
+
+ void Initialize();
+
+ // DriveServiceBridge:
+ virtual drive::DriveAppRegistry* GetAppRegistry() OVERRIDE;
+
+ // drive::DriveServiceObserver:
+ virtual void OnReadyToSendRequests() OVERRIDE;
+
+ // drive::DriveNotificationObserver:
+ virtual void OnNotificationReceived() OVERRIDE;
+ virtual void OnPushNotificationEnabled(bool enabled) OVERRIDE;
+
+ private:
+ Profile* profile_;
+ scoped_ptr<drive::DriveServiceInterface> drive_service_;
+ scoped_ptr<drive::DriveAppRegistry> drive_app_registry_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveServiceBridgeImpl);
+};
+
+DriveServiceBridgeImpl::DriveServiceBridgeImpl(Profile* profile)
+ : profile_(profile) {
+ DCHECK(profile_);
+}
+
+DriveServiceBridgeImpl::~DriveServiceBridgeImpl() {
+ drive::DriveNotificationManager* drive_notification_manager =
+ drive::DriveNotificationManagerFactory::FindForBrowserContext(profile_);
+ if (drive_notification_manager)
+ drive_notification_manager->RemoveObserver(this);
+
+ drive_service_->RemoveObserver(this);
+
+ drive_app_registry_.reset();
+ drive_service_.reset();
+}
+
+void DriveServiceBridgeImpl::Initialize() {
+ scoped_refptr<base::SequencedWorkerPool> worker_pool(
+ content::BrowserThread::GetBlockingPool());
+ scoped_refptr<base::SequencedTaskRunner> drive_task_runner(
+ worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool->GetSequenceToken(),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+
+ ProfileOAuth2TokenService* token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
+ drive_service_.reset(new drive::DriveAPIService(
+ token_service,
+ profile_->GetRequestContext(),
+ drive_task_runner.get(),
+ GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
+ GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
+ GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
+ std::string() /* custom_user_agent */));
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile_);
+ drive_service_->Initialize(signin_manager->GetAuthenticatedAccountId());
+ drive_service_->AddObserver(this);
+
+ drive::DriveNotificationManager* drive_notification_manager =
+ drive::DriveNotificationManagerFactory::GetForBrowserContext(profile_);
+ if (drive_notification_manager)
+ drive_notification_manager->AddObserver(this);
+
+ drive_app_registry_.reset(new drive::DriveAppRegistry(drive_service_.get()));
+ if (drive_service_->CanSendRequest())
+ drive_app_registry_->Update();
+}
+
+drive::DriveAppRegistry* DriveServiceBridgeImpl::GetAppRegistry() {
+ return drive_app_registry_.get();
+}
+
+void DriveServiceBridgeImpl::OnReadyToSendRequests() {
+ drive_app_registry_->Update();
+}
+
+void DriveServiceBridgeImpl::OnNotificationReceived() {
+ if (drive_service_->CanSendRequest())
+ drive_app_registry_->Update();
+}
+
+void DriveServiceBridgeImpl::OnPushNotificationEnabled(bool enabled) {
+ if (enabled && drive_service_->CanSendRequest())
+ drive_app_registry_->Update();
+}
+
+} // namespace
+
+// static
+scoped_ptr<DriveServiceBridge> DriveServiceBridge::Create(Profile* profile) {
+ scoped_ptr<DriveServiceBridgeImpl> bridge(
+ new DriveServiceBridgeImpl(profile));
+ bridge->Initialize();
+ return bridge.PassAs<DriveServiceBridge>();
+}
+
+// static
+void DriveServiceBridge::AppendDependsOnFactories(
+ std::set<BrowserContextKeyedServiceFactory*>* factories) {
+ DCHECK(factories);
+ factories->insert(ProfileOAuth2TokenServiceFactory::GetInstance());
+ factories->insert(SigninManagerFactory::GetInstance());
+ factories->insert(drive::DriveNotificationManagerFactory::GetInstance());
+}
diff --git a/chrome/browser/apps/drive/drive_service_bridge.h b/chrome/browser/apps/drive/drive_service_bridge.h
new file mode 100644
index 0000000..7bc1ea4
--- /dev/null
+++ b/chrome/browser/apps/drive/drive_service_bridge.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_DRIVE_DRIVE_SERVICE_BRIDGE_H_
+#define CHROME_BROWSER_APPS_DRIVE_DRIVE_SERVICE_BRIDGE_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace drive {
+class DriveAppRegistry;
+}
+
+class BrowserContextKeyedServiceFactory;
+class Profile;
+
+// An interface to access Drive service for a given profile.
+class DriveServiceBridge {
+ public:
+ virtual ~DriveServiceBridge() {}
+
+ // Factory to create an instance of DriveServiceBridge.
+ static scoped_ptr<DriveServiceBridge> Create(Profile* profile);
+
+ // Appends PKS factories this class depends on.
+ static void AppendDependsOnFactories(
+ std::set<BrowserContextKeyedServiceFactory*>* factories);
+
+ // Returns the DriveAppRegistery to use. The ownership is not transferred.
+ virtual drive::DriveAppRegistry* GetAppRegistry() = 0;
+};
+
+#endif // CHROME_BROWSER_APPS_DRIVE_DRIVE_SERVICE_BRIDGE_H_