Added extensions API to provide customization values.

This CL adds API that could be used only by component extensions.
Currently, API only returns device's HWID.

BUG=chromium-os:13076
TEST=browser_tests

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79264 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/chromeos/customization_document.h b/chrome/browser/chromeos/customization_document.h
index 019ca74..1917228 100644
--- a/chrome/browser/chromeos/customization_document.h
+++ b/chrome/browser/chromeos/customization_document.h
@@ -52,12 +52,12 @@
   std::string GetHelpPage(const std::string& locale) const;
   std::string GetEULAPage(const std::string& locale) const;
 
- private:
-  typedef std::map<std::string, std::string> VPDMap;
-
   // Returns HWID for the machine. Declared as virtual to override in tests.
   virtual std::string GetHWID() const;
 
+ private:
+  typedef std::map<std::string, std::string> VPDMap;
+
   // Returns VPD as string. Declared as virtual to override in tests.
   virtual std::string GetVPD() const;
 
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index bebf4576..a3e10aa1 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -100,26 +100,30 @@
 }
 
 bool ExtensionApiTest::RunExtensionTest(const char* extension_name) {
-  return RunExtensionTestImpl(extension_name, "", false, true);
+  return RunExtensionTestImpl(extension_name, "", false, true, false);
 }
 
 bool ExtensionApiTest::RunExtensionTestIncognito(const char* extension_name) {
-  return RunExtensionTestImpl(extension_name, "", true, true);
+  return RunExtensionTestImpl(extension_name, "", true, true, false);
+}
+
+bool ExtensionApiTest::RunComponentExtensionTest(const char* extension_name) {
+  return RunExtensionTestImpl(extension_name, "", false, true, true);
 }
 
 bool ExtensionApiTest::RunExtensionTestNoFileAccess(
     const char* extension_name) {
-  return RunExtensionTestImpl(extension_name, "", false, false);
+  return RunExtensionTestImpl(extension_name, "", false, false, false);
 }
 
 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
     const char* extension_name) {
-  return RunExtensionTestImpl(extension_name, "", true, false);
+  return RunExtensionTestImpl(extension_name, "", true, false, false);
 }
 bool ExtensionApiTest::RunExtensionSubtest(const char* extension_name,
                                            const std::string& page_url) {
   DCHECK(!page_url.empty()) << "Argument page_url is required.";
-  return RunExtensionTestImpl(extension_name, page_url, false, true);
+  return RunExtensionTestImpl(extension_name, page_url, false, true, false);
 }
 
 bool ExtensionApiTest::RunPageTest(const std::string& page_url) {
@@ -131,22 +135,28 @@
 bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name,
                                             const std::string& page_url,
                                             bool enable_incognito,
-                                            bool enable_fileaccess) {
+                                            bool enable_fileaccess,
+                                            bool load_as_component) {
   ResultCatcher catcher;
   DCHECK(!std::string(extension_name).empty() || !page_url.empty()) <<
       "extension_name and page_url cannot both be empty";
 
   if (!std::string(extension_name).empty()) {
-    bool loaded;
-    if (enable_incognito) {
-      loaded = enable_fileaccess ?
-        LoadExtensionIncognito(test_data_dir_.AppendASCII(extension_name)) :
-        LoadExtensionIncognitoNoFileAccess(
-            test_data_dir_.AppendASCII(extension_name));
+    bool loaded = false;
+    if (load_as_component) {
+      loaded =
+          LoadExtensionAsComponent(test_data_dir_.AppendASCII(extension_name));
     } else {
-      loaded = enable_fileaccess ?
-        LoadExtension(test_data_dir_.AppendASCII(extension_name)) :
-        LoadExtensionNoFileAccess(test_data_dir_.AppendASCII(extension_name));
+      if (enable_incognito) {
+        loaded = enable_fileaccess ?
+          LoadExtensionIncognito(test_data_dir_.AppendASCII(extension_name)) :
+          LoadExtensionIncognitoNoFileAccess(
+              test_data_dir_.AppendASCII(extension_name));
+      } else {
+        loaded = enable_fileaccess ?
+          LoadExtension(test_data_dir_.AppendASCII(extension_name)) :
+          LoadExtensionNoFileAccess(test_data_dir_.AppendASCII(extension_name));
+      }
     }
     if (!loaded) {
       message_ = "Failed to load extension.";
diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h
index 5ab3748a..52b492e 100644
--- a/chrome/browser/extensions/extension_apitest.h
+++ b/chrome/browser/extensions/extension_apitest.h
@@ -80,6 +80,9 @@
   // Same as RunExtensionTest, but enables the extension for incognito mode.
   bool RunExtensionTestIncognito(const char* extension_name);
 
+  // Same as RunExtensionTest, but loads extension as component.
+  bool RunComponentExtensionTest(const char* extension_name);
+
   // Same as RunExtensionTest, but disables file access.
   bool RunExtensionTestNoFileAccess(const char* extension_name);
 
@@ -116,7 +119,8 @@
   bool RunExtensionTestImpl(const char* extension_name,
                             const std::string& test_page,
                             bool enable_incogntio,
-                            bool enable_fileaccess);
+                            bool enable_fileaccess,
+                            bool load_as_component);
 
   // Hold details of the test, set in C++, which can be accessed by
   // javascript using chrome.test.getConfig().
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index b487dbc..a644eb7 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -111,6 +111,20 @@
   return LoadExtensionImpl(path, true, false);
 }
 
+bool ExtensionBrowserTest::LoadExtensionAsComponent(const FilePath& path) {
+  ExtensionService* service = browser()->profile()->GetExtensionService();
+
+  std::string manifest;
+  if (!file_util::ReadFileToString(path.Append(Extension::kManifestFilename),
+                                   &manifest))
+    return false;
+
+  service->LoadComponentExtension(
+      ExtensionService::ComponentExtensionInfo(manifest, path));
+
+  return true;
+}
+
 FilePath ExtensionBrowserTest::PackExtension(const FilePath& dir_path) {
   FilePath crx_path;
   if (!PathService::Get(base::DIR_TEMP, &crx_path)) {
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index c5c150a0..22fb6c6 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -28,6 +28,9 @@
   // Same as above, but enables the extension in incognito mode first.
   bool LoadExtensionIncognito(const FilePath& path);
 
+  // Loads extension and imitates that it is a component extension.
+  bool LoadExtensionAsComponent(const FilePath& path);
+
   // By default, unpacked extensions have file access: this loads them with
   // that permission removed.
   bool LoadExtensionNoFileAccess(const FilePath& path);
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index cbfc0c59..b295d69 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -65,6 +65,10 @@
 #include "chrome/browser/extensions/extension_input_api.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/extensions/extension_info_private_api_chromeos.h"
+#endif
+
 // FactoryRegistry -------------------------------------------------------------
 
 namespace {
@@ -304,6 +308,11 @@
   RegisterFunction<SetPreferenceFunction>();
   RegisterFunction<ClearPreferenceFunction>();
 
+#if defined(OS_CHROMEOS)
+  // Device Customization.
+  RegisterFunction<GetChromeosInfoFunction>();
+#endif
+
   // Debugger
   RegisterFunction<AttachDebuggerFunction>();
   RegisterFunction<DetachDebuggerFunction>();
diff --git a/chrome/browser/extensions/extension_info_private_api_chromeos.cc b/chrome/browser/extensions/extension_info_private_api_chromeos.cc
new file mode 100644
index 0000000..0982add
--- /dev/null
+++ b/chrome/browser/extensions/extension_info_private_api_chromeos.cc
@@ -0,0 +1,175 @@
+// Copyright (c) 2011 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 "extension_info_private_api_chromeos.h"
+
+#include <map>
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/cros/system_library.h"
+#include "chrome/browser/chromeos/customization_document.h"
+#include "chrome/common/extensions/extension_error_utils.h"
+#include "content/browser/browser_thread.h"
+
+namespace {
+
+// Key which corresponds to the HWID setting.
+const char kPropertyHWID[] = "hwid";
+
+// Path to OEM partner startup customization manifest.
+const char kStartupCustomizationManifestPath[] =
+    "/opt/oem/etc/startup_manifest.json";
+
+// Keeps cached values to avoid unnecessary file operations. Should only be
+// used from the UI thread.
+class CachedProperties : base::NonThreadSafe {
+ public:
+  ~CachedProperties();
+  // Gets value for the property with the given name. Return whether value has
+  // been found.
+  bool GetValue(const std::string& property_name, std::string* value);
+
+  // Updates cached properties with the given item.
+  void Update(const std::pair<std::string, std::string>& item);
+
+ private:
+  typedef std::map<std::string, std::string> PropertyMap;
+  PropertyMap cache_;
+};
+
+CachedProperties::~CachedProperties() {
+  // It is safe to delete this class on any thread since class is used
+  // through LazyInstance.
+  DetachFromThread();
+}
+
+bool CachedProperties::GetValue(const std::string& property_name,
+                                std::string* value) {
+  DCHECK(CalledOnValidThread());
+  PropertyMap::iterator iter = cache_.find(property_name);
+  if (iter != cache_.end()) {
+    (*value) = iter->second;
+    return true;
+  }
+  return false;
+}
+
+// Updates cached properties with the given value of the property.
+void CachedProperties::Update(
+    const std::pair<std::string, std::string>& item) {
+  DCHECK(CalledOnValidThread());
+  cache_.insert(item);
+}
+
+// Provides customization properties. Should be used only on the FILE thread.
+class CustomizationData : base::NonThreadSafe {
+ public:
+  CustomizationData();
+  ~CustomizationData();
+
+  // Gets value for the property with the given name. Return whether value has
+  // been found.
+  bool GetValue(const std::string& property_name, std::string* value);
+
+ private:
+  // Keeps customization document from which properties are extracted.
+  chromeos::StartupCustomizationDocument document_;
+
+  DISALLOW_COPY_AND_ASSIGN(CustomizationData);
+};
+
+CustomizationData::CustomizationData() {
+  DCHECK(CalledOnValidThread());
+  document_.LoadManifestFromFile(FilePath(kStartupCustomizationManifestPath));
+}
+
+CustomizationData::~CustomizationData() {
+  // It is safe to delete this class on any thread since class is used
+  // through LazyInstance.
+  DetachFromThread();
+}
+
+bool CustomizationData::GetValue(const std::string& property_name,
+                                 std::string* value) {
+  DCHECK(CalledOnValidThread());
+  if (property_name == kPropertyHWID) {
+    (*value) = document_.GetHWID();
+  } else {
+    LOG(ERROR) << "Unknown property request: " << property_name;
+    return false;
+  }
+  return true;
+}
+
+// Shared instances.
+base::LazyInstance<CachedProperties> g_cached_properties(
+    base::LINKER_INITIALIZED);
+base::LazyInstance<CustomizationData> g_customization(base::LINKER_INITIALIZED);
+
+}  // namespace
+
+GetChromeosInfoFunction::GetChromeosInfoFunction()
+    : result_dictionary_(new DictionaryValue) {
+}
+
+GetChromeosInfoFunction::~GetChromeosInfoFunction() {
+}
+
+bool GetChromeosInfoFunction::RunImpl() {
+  ListValue* list = NULL;
+  EXTENSION_FUNCTION_VALIDATE(args_->GetList(0, &list));
+  for (size_t i = 0; i < list->GetSize(); ++i) {
+    std::string property_name;
+    EXTENSION_FUNCTION_VALIDATE(list->GetString(i, &property_name));
+    std::string value;
+    if (g_cached_properties.Get().GetValue(property_name, &value)) {
+      result_dictionary_->Set(property_name, Value::CreateStringValue(value));
+    } else {
+      properties_.push_back(property_name);
+    }
+  }
+
+  if (!properties_.empty()) {
+    // This will calls us back on UI thread.
+    BrowserThread::PostTask(
+        BrowserThread::FILE,
+        FROM_HERE,
+        NewRunnableMethod(this,
+                          &GetChromeosInfoFunction::LoadValues));
+  } else {
+    // Early answer is ready.
+    result_.reset(result_dictionary_.release());
+    SendResponse(true);
+  }
+  return true;
+}
+
+void GetChromeosInfoFunction::RespondOnUIThread() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  for (size_t i = 0; i < new_results_.size(); ++i) {
+    result_dictionary_->Set(new_results_[i].first,
+                            Value::CreateStringValue(new_results_[i].second));
+    g_cached_properties.Get().Update(new_results_[i]);
+  }
+  result_.reset(result_dictionary_.release());
+  SendResponse(true);
+}
+
+void GetChromeosInfoFunction::LoadValues() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  new_results_.clear();
+  for (size_t i = 0; i < properties_.size(); ++i) {
+    std::string value;
+    if (g_customization.Get().GetValue(properties_[i], &value))
+      new_results_.push_back(std::make_pair(properties_[i], value));
+  }
+  BrowserThread::PostTask(
+      BrowserThread::UI,
+      FROM_HERE,
+      NewRunnableMethod(
+          this,
+          &GetChromeosInfoFunction::RespondOnUIThread));
+}
diff --git a/chrome/browser/extensions/extension_info_private_api_chromeos.h b/chrome/browser/extensions/extension_info_private_api_chromeos.h
new file mode 100644
index 0000000..fbaa76f6
--- /dev/null
+++ b/chrome/browser/extensions/extension_info_private_api_chromeos.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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_EXTENSIONS_EXTENSION_INFO_PRIVATE_API_CHROMEOS_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_INFO_PRIVATE_API_CHROMEOS_H_
+#pragma once
+
+#include <string>
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "chrome/browser/extensions/extension_function.h"
+
+class DictionaryValue;
+
+namespace chromeos {
+class StartupCustomizationDocument;
+}  // namespace chromeos
+
+class GetChromeosInfoFunction : public AsyncExtensionFunction {
+ public:
+  GetChromeosInfoFunction();
+
+ protected:
+  virtual ~GetChromeosInfoFunction();
+
+  virtual bool RunImpl();
+
+ private:
+  // This method is called on FILE thread.
+  void LoadValues();
+
+  // This method is called on UI thread.
+  void RespondOnUIThread();
+
+  scoped_ptr<DictionaryValue> result_dictionary_;
+  std::vector<std::string> properties_;
+  std::vector<std::pair<std::string, std::string> > new_results_;
+
+  DECLARE_EXTENSION_FUNCTION_NAME("chromeosInfoPrivate.get");
+};
+
+#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_INFO_PRIVATE_API_CHROMEOS_H_
diff --git a/chrome/browser/extensions/extension_info_private_apitest_chromeos.cc b/chrome/browser/extensions/extension_info_private_apitest_chromeos.cc
new file mode 100644
index 0000000..3943b5a3
--- /dev/null
+++ b/chrome/browser/extensions/extension_info_private_apitest_chromeos.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2011 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/extensions/extension_apitest.h"
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CustomizationPrivateTest) {
+  ASSERT_TRUE(RunComponentExtensionTest("chromeos_info_private")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CustomizationPrivateFailTest) {
+  // Only component extensions can use it.
+  ASSERT_FALSE(RunExtensionTest("chromeos_info_private")) << message_;
+}
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 9e0145b..634926744 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -835,29 +835,34 @@
   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, NULL));
-    if (!manifest.get()) {
-      DLOG(ERROR) << "Failed to parse manifest for extension";
-      continue;
-    }
-
-    std::string error;
-    scoped_refptr<const Extension> extension(Extension::Create(
-        it->root_directory,
-        Extension::COMPONENT,
-        *static_cast<DictionaryValue*>(manifest.get()),
-        true,  // Require key
-        Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT),
-        &error));
-    if (!extension.get()) {
-      NOTREACHED() << error;
-      return;
-    }
-    AddExtension(extension);
+    LoadComponentExtension(*it);
   }
 }
 
+void ExtensionService::LoadComponentExtension(
+    const ComponentExtensionInfo &info) {
+  JSONStringValueSerializer serializer(info.manifest);
+  scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
+  if (!manifest.get()) {
+    DLOG(ERROR) << "Failed to parse manifest for extension";
+    return;
+  }
+
+  std::string error;
+  scoped_refptr<const Extension> extension(Extension::Create(
+      info.root_directory,
+      Extension::COMPONENT,
+      *static_cast<DictionaryValue*>(manifest.get()),
+      true,  // Require key
+      Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT),
+      &error));
+  if (!extension.get()) {
+    NOTREACHED() << error;
+    return;
+  }
+  AddExtension(extension);
+}
+
 void ExtensionService::LoadAllExtensions() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 5e83300..a6081d6 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -250,13 +250,16 @@
   // extension.
   void GrantPermissionsAndEnableExtension(const Extension* extension);
 
-  // Load the extension from the directory |extension_path|.
+  // Loads the extension from the directory |extension_path|.
   void LoadExtension(const FilePath& extension_path);
 
-  // Load any component extensions.
+  // Loads any component extensions.
   void LoadComponentExtensions();
 
-  // Load all known extensions (used by startup and testing code).
+  // Loads particular component extension.
+  void LoadComponentExtension(const ComponentExtensionInfo& info);
+
+  // Loads all known extensions (used by startup and testing code).
   void LoadAllExtensions();
 
   // Continues loading all know extensions. It can be called from
diff --git a/chrome/browser/resources/help_app/manifest.json b/chrome/browser/resources/help_app/manifest.json
index 368171e..d34b2a1 100644
--- a/chrome/browser/resources/help_app/manifest.json
+++ b/chrome/browser/resources/help_app/manifest.json
@@ -10,6 +10,7 @@
   "default_locale": "en",
   "incognito": "split",
   "permissions": [
+    "chromeosInfoPrivate",
     "tabs",
     "http://www.google.com/support/chromeos/*/*",
     "https://www.google.com/support/chromeos/*/*"