Load the bookmark manager extension at Chrome startup.

Review URL: http://codereview.chromium.org/660232

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40683 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index fb746ab..fd1886d 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- This comment is only here because changes to resources are not picked up
-without changes to the corresponding grd file.  taaaag -->
+without changes to the corresponding grd file. aa1 -->
 <grit latest_public_release="0" current_release="1">
   <outputs>
     <output filename="grit/browser_resources.h" type="rc_header">
@@ -42,6 +42,7 @@
   <include name="IDR_FILEBROWSE_HTML" file="resources\filebrowse.html" flattenhtml="true" type="BINDATA" />
   <include name="IDR_OS_CREDITS_HTML" file="resources\about_os_credits.html" flattenhtml="true" type="BINDATA" />
       </if>
+      <include name="IDR_BOOKMARKS_MANIFEST" file="resources\bookmark_manager\manifest.json" type="BINDATA" />
       <include name="IDR_DOWNLOADS_HTML" file="resources\downloads.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_LOCAL_STRINGS_JS" file="resources\local_strings.js" type="BINDATA" />
       <include name="IDR_DOM_UI_CSS" file="resources\dom_ui.css" flattenhtml="true" type="BINDATA" />
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index e5a0768..a842c97 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -87,13 +87,25 @@
 // Test that exactly one extension loaded.
 Extension* ExtensionApiTest::GetSingleLoadedExtension() {
   ExtensionsService* service = browser()->profile()->GetExtensionsService();
-  if (service->extensions()->size() != 1u) {
-    message_ = StringPrintf(
-        "Expected only one extension to be present.  Found %u.",
-        static_cast<unsigned>(service->extensions()->size()));
-    return NULL;
+
+  int found_extension_index = -1;
+  for (size_t i = 0; i < service->extensions()->size(); ++i) {
+    // Ignore any component extensions. They are automatically loaded into all
+    // profiles and aren't the extension we're looking for here.
+    if (service->extensions()->at(i)->location() == Extension::COMPONENT)
+      continue;
+
+    if (found_extension_index != -1) {
+      message_ = StringPrintf(
+          "Expected only one extension to be present.  Found %u.",
+          static_cast<unsigned>(service->extensions()->size()));
+      return NULL;
+    }
+
+    found_extension_index = static_cast<int>(i);
   }
-  Extension* extension = service->extensions()->at(0);
+
+  Extension* extension = service->extensions()->at(found_extension_index);
   if (!extension) {
     message_ = "extension pointer is NULL.";
     return NULL;
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 3aeda24..388172d 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -538,15 +538,23 @@
       NOTREACHED();
       continue;
     }
+
+    // Only internal and external extensions can be installed permanently in the
+    // preferences.
+    Extension::Location location =
+        static_cast<Extension::Location>(location_value);
+    if (location != Extension::INTERNAL &&
+        !Extension::IsExternalLocation(location)) {
+      NOTREACHED();
+      continue;
+    }
+
     DictionaryValue* manifest = NULL;
     if (!ext->GetDictionary(kPrefManifest, &manifest)) {
       LOG(WARNING) << "Missing manifest for extension " << *extension_id;
       // Just a warning for now.
     }
 
-    Extension::Location location =
-        static_cast<Extension::Location>(location_value);
-
     extensions_info->push_back(linked_ptr<ExtensionInfo>(new ExtensionInfo(
         manifest, WideToASCII(*extension_id), FilePath(path), location)));
   }
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
index 8480ac2..96e4282 100644
--- a/chrome/browser/extensions/extension_updater.cc
+++ b/chrome/browser/extensions/extension_updater.cc
@@ -553,6 +553,13 @@
   for (ExtensionList::const_iterator iter = extensions->begin();
        iter != extensions->end(); ++iter) {
     Extension* extension = (*iter);
+
+    // Only internal and external extensions can be autoupdated.
+    if (extension->location() != Extension::INTERNAL &&
+        !Extension::IsExternalLocation(extension->location())) {
+      continue;
+    }
+
     const GURL& update_url = extension->update_url();
 
     // Collect histogram data and skip extensions with no update url.
diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/extension_updater_unittest.cc
index bc283876..3f89d07 100644
--- a/chrome/browser/extensions/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/extension_updater_unittest.cc
@@ -126,6 +126,7 @@
     FilePath path(StringPrintf("/extension%i", i));
 #endif
     Extension* e = new Extension(path);
+    e->set_location(Extension::INTERNAL);
     input.SetString(extension_manifest_keys::kVersion,
                     StringPrintf("%d.0.0.0", i));
     input.SetString(extension_manifest_keys::kName,
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 77f7f48..e94d9bd1 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -38,6 +38,7 @@
 #include "chrome/common/extensions/extension_l10n_util.h"
 #include "chrome/common/notification_service.h"
 #include "chrome/common/notification_type.h"
+#include "chrome/common/json_value_serializer.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "webkit/database/database_tracker.h"
@@ -321,9 +322,37 @@
           extension_path, scoped_refptr<ExtensionsService>(this)));
 }
 
+void ExtensionsService::LoadComponentExtensions() {
+  for (RegisteredComponentExtensions::iterator it =
+           component_extension_manifests_.begin();
+       it != component_extension_manifests_.end(); ++it) {
+    JSONStringValueSerializer serializer(it->manifest);
+    scoped_ptr<Value> manifest(serializer.Deserialize(NULL));
+    DCHECK(manifest.get());
+
+    scoped_ptr<Extension> extension(new Extension(it->root_directory));
+    extension->set_location(Extension::COMPONENT);
+
+    std::string error;
+    if (!extension->InitFromValue(
+            *static_cast<DictionaryValue*>(manifest.get()),
+            true,  // require key
+            &error)) {
+      NOTREACHED();
+      return;
+    }
+
+    OnExtensionLoaded(extension.release(), false);  // Don't allow privilege
+                                                    // increase.
+  }
+}
+
 void ExtensionsService::LoadAllExtensions() {
   base::TimeTicks start_time = base::TimeTicks::Now();
 
+  // Load any component extensions.
+  LoadComponentExtensions();
+
   // Load the previously installed extensions.
   scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
       ExtensionPrefs::CollectExtensionsInfo(extension_prefs_.get()));
@@ -363,43 +392,46 @@
   UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
   UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
 
-  if (extensions_.size()) {
-    UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
-                        base::TimeTicks::Now() - start_time);
+  UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
+                      base::TimeTicks::Now() - start_time);
 
-    int user_script_count = 0;
-    int extension_count = 0;
-    int theme_count = 0;
-    int external_count = 0;
-    int page_action_count = 0;
-    int browser_action_count = 0;
-    ExtensionList::iterator ex;
-    for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
-      if ((*ex)->IsTheme()) {
-        theme_count++;
-      } else if ((*ex)->converted_from_user_script()) {
-        user_script_count++;
-      } else {
-        extension_count++;
-      }
-      if (Extension::IsExternalLocation((*ex)->location())) {
-        external_count++;
-      }
-      if ((*ex)->page_action() != NULL) {
-        page_action_count++;
-      }
-      if ((*ex)->browser_action() != NULL) {
-        browser_action_count++;
-      }
+  int user_script_count = 0;
+  int extension_count = 0;
+  int theme_count = 0;
+  int external_count = 0;
+  int page_action_count = 0;
+  int browser_action_count = 0;
+  ExtensionList::iterator ex;
+  for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
+    // Don't count component extensions, since they are only extensions as an
+    // implementation detail.
+    if ((*ex)->location() == Extension::COMPONENT)
+      continue;
+
+    if ((*ex)->IsTheme()) {
+      theme_count++;
+    } else if ((*ex)->converted_from_user_script()) {
+      user_script_count++;
+    } else {
+      extension_count++;
     }
-    UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
-    UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
-    UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
-    UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
-    UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
-    UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
-                             browser_action_count);
+    if (Extension::IsExternalLocation((*ex)->location())) {
+      external_count++;
+    }
+    if ((*ex)->page_action() != NULL) {
+      page_action_count++;
+    }
+    if ((*ex)->browser_action() != NULL) {
+      browser_action_count++;
+    }
   }
+  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
+                           browser_action_count);
 }
 
 void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index a95d0aa..6501824 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -66,6 +66,22 @@
       public ExtensionUpdateService,
       public NotificationObserver {
  public:
+  // Information about a registered component extension.
+  struct ComponentExtensionInfo {
+    ComponentExtensionInfo(const std::string& manifest,
+                           const FilePath& root_directory)
+        : manifest(manifest),
+          root_directory(root_directory) {
+    }
+
+    // The extension's manifest. This is required for component extensions so
+    // that ExtensionsService doesn't need to go to disk to load them.
+    std::string manifest;
+
+    // Directory where the extension is stored.
+    FilePath root_directory;
+  };
+
   // The name of the directory inside the profile where extensions are
   // installed to.
   static const char* kInstallDirectoryName;
@@ -97,6 +113,11 @@
     return &disabled_extensions_;
   }
 
+  // Registers an extension to be loaded as a component extension.
+  void register_component_extension(const ComponentExtensionInfo& info) {
+    component_extension_manifests_.push_back(info);
+  }
+
   // Returns true if any extensions are installed.
   virtual bool HasInstalledExtensions() {
     return !(extensions_.empty() && disabled_extensions_.empty());
@@ -155,6 +176,9 @@
   // Load the extension from the directory |extension_path|.
   void LoadExtension(const FilePath& extension_path);
 
+  // Load any component extensions.
+  void LoadComponentExtensions();
+
   // Load all known extensions (used by startup and testing code).
   void LoadAllExtensions();
 
@@ -330,6 +354,10 @@
 
   NotificationRegistrar registrar_;
 
+  // List of registered component extensions (see Extension::Location).
+  typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions;
+  RegisteredComponentExtensions component_extension_manifests_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionsService);
 };
 
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index b2ea08a3..eb2520a 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -1684,3 +1684,42 @@
   EXPECT_EQ(profile_->GetDatabaseTracker()->GetDefaultQuota(), limited_quota);
   EXPECT_EQ(kint64max, unlimited_quota);
 }
+
+// Tests ExtensionsService::register_component_extension().
+TEST_F(ExtensionsServiceTest, ComponentExtensions) {
+  InitializeEmptyExtensionsService();
+
+  FilePath path;
+  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
+  path = path.AppendASCII("extensions")
+      .AppendASCII("good")
+      .AppendASCII("Extensions")
+      .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
+      .AppendASCII("1.0.0.0");
+
+  std::string manifest;
+  ASSERT_TRUE(file_util::ReadFileToString(
+      path.Append(Extension::kManifestFilename), &manifest));
+
+  service_->register_component_extension(
+      ExtensionsService::ComponentExtensionInfo(manifest, path));
+  service_->Init();
+
+  // Note that we do not pump messages -- the extension should be loaded
+  // immediately.
+
+  EXPECT_EQ(0u, GetErrors().size());
+  ASSERT_EQ(1u, loaded_.size());
+  EXPECT_EQ(Extension::COMPONENT, loaded_[0]->location());
+  EXPECT_EQ(1u, service_->extensions()->size());
+
+  // Component extensions shouldn't get recourded in the prefs.
+  ValidatePrefKeyCount(0);
+
+  // Reload all extensions, and make sure it comes back.
+  std::string extension_id = service_->extensions()->at(0)->id();
+  loaded_.clear();
+  service_->ReloadExtensions();
+  ASSERT_EQ(1u, service_->extensions()->size());
+  EXPECT_EQ(extension_id, service_->extensions()->at(0)->id());
+}
diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc
index d1dfb9c..cc1224d 100644
--- a/chrome/browser/extensions/extensions_ui.cc
+++ b/chrome/browser/extensions/extensions_ui.cc
@@ -48,6 +48,24 @@
 #include "net/base/net_util.h"
 #include "webkit/glue/image_decoder.h"
 
+namespace {
+
+static bool ShouldShowExtension(Extension* extension) {
+  // Don't show the themes since this page's UI isn't really useful for
+  // themes.
+  if (extension->IsTheme())
+    return false;
+
+  // Don't show component extensions because they are only extensions as an
+  // implementation detail of Chrome.
+  if (extension->location() == Extension::COMPONENT)
+    return false;
+
+  return true;
+}
+
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //
 // ExtensionsHTMLSource
@@ -289,9 +307,7 @@
   const ExtensionList* extensions = extensions_service_->extensions();
   for (ExtensionList::const_iterator extension = extensions->begin();
        extension != extensions->end(); ++extension) {
-    // Don't show the themes since this page's UI isn't really useful for
-    // themes.
-    if (!(*extension)->IsTheme()) {
+    if (ShouldShowExtension(*extension)) {
       extensions_list->Append(CreateExtensionDetailValue(
           extensions_service_.get(),
           *extension, GetActivePagesForExtension((*extension)->id()), true));
@@ -301,7 +317,7 @@
   extensions = extensions_service_->disabled_extensions();
   for (ExtensionList::const_iterator extension = extensions->begin();
        extension != extensions->end(); ++extension) {
-    if (!(*extension)->IsTheme()) {
+    if (ShouldShowExtension(*extension)) {
       extensions_list->Append(CreateExtensionDetailValue(
           extensions_service_.get(),
           *extension, GetActivePagesForExtension((*extension)->id()), false));
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 6a3bd8c..1e248e2 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/profile.h"
 
+#include "app/resource_bundle.h"
 #include "app/theme_provider.h"
 #include "base/command_line.h"
 #include "base/file_path.h"
@@ -60,6 +61,7 @@
 #include "chrome/common/notification_service.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
+#include "grit/browser_resources.h"
 #include "grit/locale_settings.h"
 #include "net/base/transport_security_state.h"
 #include "webkit/database/database_tracker.h"
@@ -660,6 +662,23 @@
       GetPath().AppendASCII(ExtensionsService::kInstallDirectoryName),
       true);
 
+  // Register the bookmark manager extension.
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableTabbedBookmarkManager)) {
+    FilePath bookmark_manager_path;
+    if (PathService::Get(chrome::DIR_BOOKMARK_MANAGER,
+                         &bookmark_manager_path)) {
+      std::string manifest =
+          ResourceBundle::GetSharedInstance().GetRawDataResource(
+              IDR_BOOKMARKS_MANIFEST).as_string();
+      extensions_service_->register_component_extension(
+          ExtensionsService::ComponentExtensionInfo(manifest,
+                                                    bookmark_manager_path));
+    } else {
+      NOTREACHED();
+    }
+  }
+
   extensions_service_->Init();
 
   // Load any extensions specified with --load-extension.
diff --git a/chrome/browser/resources/bookmark_manager/manifest.json b/chrome/browser/resources/bookmark_manager/manifest.json
index 9ebe6f4..aeb0afd 100644
--- a/chrome/browser/resources/bookmark_manager/manifest.json
+++ b/chrome/browser/resources/bookmark_manager/manifest.json
@@ -1,4 +1,5 @@
 {
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfHy1M+jghaHyaVAILzx/c/Dy+RXtcaP9/5pC7EY8JlNEI/G4DIIng9IzlrH8UWStpMWMyGUsdyusn2PkYFrqfVzhc2azVF3PX9D0KHG3FLN3mNoz1YTBHvO5QSXJf292qW0tTYuoGqeTfXtF9odLdg20Xd0YrLmtS4TQkpSYGDwIDAQAB",
   "name": "Bookmark Manager",
   "version": "0.1",
   "description": "Bookmark Manager",
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 7efea0c..f6b8fd6 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -415,6 +415,89 @@
       ],
     },
     {
+      'target_name': 'component_extensions',
+      'type': 'none',
+      'msvs_guid': '50B52703-525F-404C-BFE2-C46D3375D73E',
+      # TODO(aa): Once the linux port supports it, change this to recursively
+      # copy the entire directory instead of listing the files.
+      # http://crbug.com/37340.
+      'copies': [
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager',
+          'files': [
+            'browser/resources/bookmark_manager/main.html',
+            'browser/resources/bookmark_manager/manifest.json',
+          ]
+        },
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager/css',
+          'files': [
+            'browser/resources/bookmark_manager/css/bmm.css',
+            'browser/resources/bookmark_manager/css/bmm.css.js',
+            'browser/resources/bookmark_manager/css/list.css',
+            'browser/resources/bookmark_manager/css/menu.css',
+            'browser/resources/bookmark_manager/css/tree.css',
+            'browser/resources/bookmark_manager/css/tree.css.js',
+          ]
+        },
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager/js',
+          'files': [
+            'browser/resources/bookmark_manager/js/bmm.js',
+            'browser/resources/bookmark_manager/js/cr.js',
+            'browser/resources/bookmark_manager/js/i18ntemplate.js',
+            'browser/resources/bookmark_manager/js/localstrings.js',
+            'browser/resources/bookmark_manager/js/util.js',
+          ]
+        },
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager/js/cr',
+          'files': [
+            'browser/resources/bookmark_manager/js/cr/event.js',
+            'browser/resources/bookmark_manager/js/cr/eventtarget.js',
+            'browser/resources/bookmark_manager/js/cr/promise.js',
+            'browser/resources/bookmark_manager/js/cr/ui.js',
+          ]
+        },
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager/js/cr/ui',
+          'files': [
+            'browser/resources/bookmark_manager/js/cr/ui/menuitem.js',
+            'browser/resources/bookmark_manager/js/cr/ui/command.js',
+            'browser/resources/bookmark_manager/js/cr/ui/menubutton.js',
+            'browser/resources/bookmark_manager/js/cr/ui/list.js',
+            'browser/resources/bookmark_manager/js/cr/ui/tree.js',
+            'browser/resources/bookmark_manager/js/cr/ui/listselectionmodel.js',
+            'browser/resources/bookmark_manager/js/cr/ui/menu.js',
+            'browser/resources/bookmark_manager/js/cr/ui/listitem.js',
+            'browser/resources/bookmark_manager/js/cr/ui/contextmenuhandler.js',
+          ]
+        },
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager/js/bmm',
+          'files': [
+            'browser/resources/bookmark_manager/js/bmm/bookmarklist.js',
+            'browser/resources/bookmark_manager/js/bmm/bookmarktree.js',
+            'browser/resources/bookmark_manager/js/bmm/treeiterator.js',
+          ]
+        },
+        {
+          'destination': '<(PRODUCT_DIR)/resources/bookmark_manager/images',
+          'files': [
+            'browser/resources/bookmark_manager/images/folder_open_rtl.png',
+            'browser/resources/bookmark_manager/images/folder_open.png',
+            'browser/resources/bookmark_manager/images/bookmark_manager_recent.png',
+            'browser/resources/bookmark_manager/images/bookmark_bar_folder_mac.png',
+            'browser/resources/bookmark_manager/images/bookmarks_favicon.png',
+            'browser/resources/bookmark_manager/images/bookmarks_section.png',
+            'browser/resources/bookmark_manager/images/folder_closed.png',
+            'browser/resources/bookmark_manager/images/bookmark_manager_search.png',
+            'browser/resources/bookmark_manager/images/folder_closed_rtl.png',
+          ]
+        },
+      ]
+    },
+    {
       'target_name': 'debugger',
       'type': '<(library)',
       'msvs_guid': '57823D8C-A317-4713-9125-2C91FDFD12D6',
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index d423507..58e8f68 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -111,7 +111,7 @@
       if (!GetUserDesktop(&cur))
         return false;
       break;
-    case chrome::DIR_INSPECTOR:
+    case chrome::DIR_RESOURCES:
 #if defined(OS_MACOSX)
       cur = mac_util::MainAppBundlePath();
       cur = cur.Append(FILE_PATH_LITERAL("Resources"));
@@ -120,6 +120,15 @@
         return false;
       cur = cur.Append(FILE_PATH_LITERAL("resources"));
 #endif
+      break;
+    case chrome::DIR_BOOKMARK_MANAGER:
+      if (!PathService::Get(chrome::DIR_RESOURCES, &cur))
+        return false;
+      cur = cur.Append(FILE_PATH_LITERAL("bookmark_manager"));
+      break;
+    case chrome::DIR_INSPECTOR:
+      if (!PathService::Get(chrome::DIR_RESOURCES, &cur))
+        return false;
       cur = cur.Append(FILE_PATH_LITERAL("inspector"));
       break;
     case chrome::DIR_APP_DICTIONARIES:
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index 3b0521b..2bfaeea 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -15,38 +15,41 @@
 enum {
   PATH_START = 1000,
 
-  DIR_APP = PATH_START,         // directory where dlls and data reside
-  DIR_LOGS,                     // directory where logs should be written
-  DIR_USER_DATA,                // directory where user data can be written
-  DIR_CRASH_DUMPS,              // directory where crash dumps are written
-  DIR_USER_DESKTOP,             // directory that correspond to the desktop
-  DIR_INSPECTOR,                // directory where web inspector is located
-  DIR_APP_DICTIONARIES,         // directory where the global dictionaries are
-  DIR_USER_DOCUMENTS,           // directory for a user's "My Documents"
-  DIR_DEFAULT_DOWNLOADS_SAFE,   // directory for a user's
-                                // "My Documents/Downloads"
-  DIR_DEFAULT_DOWNLOADS,        // directory for a user's downloads
-  FILE_RESOURCE_MODULE,         // full path and filename of the module that
+  DIR_APP = PATH_START,         // Directory where dlls and data reside.
+  DIR_LOGS,                     // Directory where logs should be written.
+  DIR_USER_DATA,                // Directory where user data can be written.
+  DIR_CRASH_DUMPS,              // Directory where crash dumps are written.
+  DIR_USER_DESKTOP,             // Directory that correspond to the desktop.
+  DIR_RESOURCES,                // Directory containing separate file resources
+                                // used by Chrome at runtime.
+  DIR_BOOKMARK_MANAGER,         // Directory containing the bookmark manager.
+  DIR_INSPECTOR,                // Directory where web inspector is located.
+  DIR_APP_DICTIONARIES,         // Directory where the global dictionaries are.
+  DIR_USER_DOCUMENTS,           // Directory for a user's "My Documents".
+  DIR_DEFAULT_DOWNLOADS_SAFE,   // Directory for a user's
+                                // "My Documents/Downloads".
+  DIR_DEFAULT_DOWNLOADS,        // Directory for a user's downloads.
+  FILE_RESOURCE_MODULE,         // Full path and filename of the module that
                                 // contains embedded resources (version,
-                                // strings, images, etc.)
-  FILE_LOCAL_STATE,             // path and filename to the file in which
-                                // machine/installation-specific state is saved
-  FILE_RECORDED_SCRIPT,         // full path to the script.log file that
-                                // contains recorded browser events for playback
-  FILE_GEARS_PLUGIN,            // full path to the gears.dll plugin file.
-  FILE_LIBAVCODEC,              // full path to libavcodec media decoding
+                                // strings, images, etc.).
+  FILE_LOCAL_STATE,             // Path and filename to the file in which
+                                // machine/installation-specific state is saved.
+  FILE_RECORDED_SCRIPT,         // Full path to the script.log file that
+                                // contains recorded browser events for playback.
+  FILE_GEARS_PLUGIN,            // Full path to the gears.dll plugin file.
+  FILE_LIBAVCODEC,              // Full path to libavcodec media decoding
                                 // library.
-  FILE_LIBAVFORMAT,             // full path to libavformat media parsing
+  FILE_LIBAVFORMAT,             // Full path to libavformat media parsing
                                 // library.
-  FILE_LIBAVUTIL,               // full path to libavutil media utility library.
+  FILE_LIBAVUTIL,               // Full path to libavutil media utility library.
 #if defined(OS_CHROMEOS)
-  FILE_CHROMEOS_API,            // full path to chrome os api shared object.
+  FILE_CHROMEOS_API,            // Full path to chrome os api shared object.
 #endif
 
 
   // Valid only in development environment; TODO(darin): move these
-  DIR_TEST_DATA,                // directory where unit test data resides
-  DIR_TEST_TOOLS,               // directory where unit test tools reside
+  DIR_TEST_DATA,                // Directory where unit test data resides.
+  DIR_TEST_TOOLS,               // Directory where unit test tools reside.
 
   PATH_END
 };
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 0130929..4436b72 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -58,25 +58,6 @@
     (*id)[i] = HexStringToInt(id->substr(i, 1)) + 'a';
 }
 
-// Returns true if the given string is an API permission (see kPermissionNames).
-static bool IsAPIPermission(const std::string& str) {
-  for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
-    if (str == Extension::kPermissionNames[i]) {
-      if (str == Extension::kExperimentalPermission &&
-          !CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kEnableExperimentalExtensionApis) &&
-          // TODO(arv): Tighten this so that not all extensions can access the
-          // experimental APIs.
-          !CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kEnableTabbedBookmarkManager)) {
-        return false;
-      }
-      return true;
-    }
-  }
-  return false;
-}
-
 }  // namespace
 
 const FilePath::CharType Extension::kManifestFilename[] =
@@ -177,19 +158,6 @@
   return ret_val;
 }
 
-Extension::Location Extension::ExternalExtensionInstallType(
-    std::string registry_path) {
-#if defined(OS_WIN)
-  HKEY reg_root = HKEY_LOCAL_MACHINE;
-  RegKey key;
-  registry_path.append("\\");
-  registry_path.append(id_);
-  if (key.Open(reg_root, ASCIIToWide(registry_path).c_str()))
-    return Extension::EXTERNAL_REGISTRY;
-#endif
-  return Extension::EXTERNAL_PREF;
-}
-
 bool Extension::GenerateId(const std::string& input, std::string* output) {
   CHECK(output);
   if (input.length() == 0)
@@ -788,7 +756,7 @@
   result->swap(decoded);
 }
 
-bool Extension::InitFromValue(const DictionaryValue& source, bool require_id,
+bool Extension::InitFromValue(const DictionaryValue& source, bool require_key,
                               std::string* error) {
   if (source.HasKey(keys::kPublicKey)) {
     std::string public_key_bytes;
@@ -798,7 +766,7 @@
       *error = errors::kInvalidKey;
       return false;
     }
-  } else if (require_id) {
+  } else if (require_key) {
     *error = errors::kInvalidKey;
     return false;
   } else {
@@ -1447,3 +1415,25 @@
 
   return false;
 }
+
+bool Extension::IsAPIPermission(const std::string& str) {
+  for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
+    if (str == Extension::kPermissionNames[i]) {
+      // Only allow the experimental API permission if the command line
+      // flag is present, or if the extension is a component of Chrome.
+      if (str == Extension::kExperimentalPermission) {
+        if (CommandLine::ForCurrentProcess()->HasSwitch(
+                switches::kEnableExperimentalExtensionApis)) {
+          return true;
+        } else if (location() == Extension::COMPONENT) {
+          return true;
+        } else {
+          return false;
+        }
+      } else {
+        return true;
+      }
+    }
+  }
+  return false;
+}
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 88d093f..78e75a6 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -28,13 +28,18 @@
   typedef std::map<const std::string, GURL> URLOverrideMap;
 
   // What an extension was loaded from.
+  // NOTE: These values are stored as integers in the preferences, so you
+  // really don't want to change any existing ones.
   enum Location {
     INVALID,
     INTERNAL,           // A crx file from the internal Extensions directory.
     EXTERNAL_PREF,      // A crx file from an external directory (via prefs).
     EXTERNAL_REGISTRY,  // A crx file from an external directory (via eg the
                         // registry on Windows).
-    LOAD                // --load-extension.
+    LOAD,               // --load-extension.
+    COMPONENT           // An integral component of Chrome itself, which happens
+                        // to be implemented as an extension. We don't show
+                        // these in the management UI.
   };
 
   enum State {
@@ -188,9 +193,10 @@
                                  scoped_ptr<SkBitmap>* result);
 
   // Initialize the extension from a parsed manifest.
-  // If |require_id| is true, will return an error if the "id" key is missing
-  // from the value.
-  bool InitFromValue(const DictionaryValue& value, bool require_id,
+  // Usually, the id of an extension is generated by the "key" property of
+  // its manifest, but if |require_key| is |false|, a temporary ID will be
+  // generated based on the path.
+  bool InitFromValue(const DictionaryValue& value, bool require_key,
                      std::string* error);
 
   const FilePath& path() const { return path_; }
@@ -247,10 +253,6 @@
   const GURL& update_url() const { return update_url_; }
   const std::map<int, std::string>& icons() const { return icons_; }
 
-  // Returns the origin of this extension. This function takes a |registry_path|
-  // so that the registry location can be overwritten during testing.
-  Location ExternalExtensionInstallType(std::string registry_path);
-
   // Theme-related.
   DictionaryValue* GetThemeImages() const { return theme_images_.get(); }
   DictionaryValue* GetThemeColors() const { return theme_colors_.get(); }
@@ -334,6 +336,10 @@
   // Helper method to verify the app section of the manifest.
   bool LoadAppHelper(const DictionaryValue* app, std::string* error);
 
+  // Returns true if the string is one of the known api permissions (see
+  // kPermissionNames).
+  bool IsAPIPermission(const std::string& permission);
+
   // The absolute path to the directory the extension is stored in.
   FilePath path_;
 
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index d0d1f89b..29ba032e 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -25,6 +25,17 @@
 class ExtensionTest : public testing::Test {
 };
 
+// We persist location values in the preferences, so this is a sanity test that
+// someone doesn't accidentally change them.
+TEST(ExtensionTest, LocationValuesTest) {
+  ASSERT_EQ(0, Extension::INVALID);
+  ASSERT_EQ(1, Extension::INTERNAL);
+  ASSERT_EQ(2, Extension::EXTERNAL_PREF);
+  ASSERT_EQ(3, Extension::EXTERNAL_REGISTRY);
+  ASSERT_EQ(4, Extension::LOAD);
+  ASSERT_EQ(5, Extension::COMPONENT);
+}
+
 TEST(ExtensionTest, InitFromValueInvalid) {
 #if defined(OS_WIN)
   FilePath path(FILE_PATH_LITERAL("c:\\foo"));