summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/extension.cc41
-rw-r--r--chrome/browser/extensions/extension.h33
-rw-r--r--chrome/browser/extensions/extensions_service.cc130
-rw-r--r--chrome/browser/extensions/extensions_service.h106
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc98
5 files changed, 376 insertions, 32 deletions
diff --git a/chrome/browser/extensions/extension.cc b/chrome/browser/extensions/extension.cc
index d4fbe9f..b2db579 100644
--- a/chrome/browser/extensions/extension.cc
+++ b/chrome/browser/extensions/extension.cc
@@ -7,30 +7,35 @@
#include "base/logging.h"
#include "base/string_util.h"
-const std::wstring Extension::kFormatVersionKey(L"format_version");
-const std::wstring Extension::kIdKey(L"id");
-const std::wstring Extension::kNameKey(L"name");
-const std::wstring Extension::kDescriptionKey(L"description");
-const std::wstring Extension::kContentScriptsKey(L"content_scripts");
-
-const std::wstring Extension::kInvalidFormatVersionError(
+const FilePath::CharType* Extension::kManifestFilename =
+ FILE_PATH_LITERAL("manifest");
+
+const wchar_t* Extension::kFormatVersionKey = L"format_version";
+const wchar_t* Extension::kIdKey = L"id";
+const wchar_t* Extension::kNameKey = L"name";
+const wchar_t* Extension::kDescriptionKey = L"description";
+const wchar_t* Extension::kContentScriptsKey = L"content_scripts";
+
+const wchar_t* Extension::kInvalidManifestError =
+ L"Manifest is missing or invalid.";
+const wchar_t* Extension::kInvalidFormatVersionError =
StringPrintf(L"Required key '%ls' is missing or invalid",
- kFormatVersionKey.c_str()));
-const std::wstring Extension::kInvalidIdError(
+ kFormatVersionKey).c_str();
+const wchar_t* Extension::kInvalidIdError =
StringPrintf(L"Required key '%ls' is missing or invalid.",
- kIdKey.c_str()));
-const std::wstring Extension::kInvalidNameError(
+ kIdKey).c_str();
+const wchar_t* Extension::kInvalidNameError =
StringPrintf(L"Required key '%ls' is missing or has invalid type.",
- kNameKey.c_str()));
-const std::wstring Extension::kInvalidDescriptionError(
+ kNameKey).c_str();
+const wchar_t* Extension::kInvalidDescriptionError =
StringPrintf(L"Invalid type for '%ls' key.",
- kDescriptionKey.c_str()));
-const std::wstring Extension::kInvalidContentScriptsListError(
+ kDescriptionKey).c_str();
+const wchar_t* Extension::kInvalidContentScriptsListError =
StringPrintf(L"Invalid type for '%ls' key.",
- kContentScriptsKey.c_str()));
-const std::wstring Extension::kInvalidContentScriptError(
+ kContentScriptsKey).c_str();
+const wchar_t* Extension::kInvalidContentScriptError =
StringPrintf(L"Invalid type for %ls at index ",
- kContentScriptsKey.c_str()));
+ kContentScriptsKey).c_str();
bool Extension::InitFromValue(const DictionaryValue& source,
std::wstring* error) {
diff --git a/chrome/browser/extensions/extension.h b/chrome/browser/extensions/extension.h
index 16260e0..94127ac 100644
--- a/chrome/browser/extensions/extension.h
+++ b/chrome/browser/extensions/extension.h
@@ -2,12 +2,13 @@
// 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_H__
-#define CHROME_BROWSER_EXTENSIONS_EXTENSION_H__
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_H_
#include <string>
#include <vector>
+#include "base/file_path.h"
#include "base/string16.h"
#include "base/values.h"
@@ -19,20 +20,24 @@ class Extension {
// The format for extension manifests that this code understands.
static const int kExpectedFormatVersion = 1;
+ // The name of the manifest inside an extension.
+ static const FilePath::CharType* kManifestFilename;
+
// Keys used in JSON representation of extensions.
- static const std::wstring kFormatVersionKey;
- static const std::wstring kIdKey;
- static const std::wstring kNameKey;
- static const std::wstring kDescriptionKey;
- static const std::wstring kContentScriptsKey;
+ static const wchar_t* kFormatVersionKey;
+ static const wchar_t* kIdKey;
+ static const wchar_t* kNameKey;
+ static const wchar_t* kDescriptionKey;
+ static const wchar_t* kContentScriptsKey;
// Error messages returned from InitFromValue().
- static const std::wstring kInvalidFormatVersionError;
- static const std::wstring kInvalidIdError;
- static const std::wstring kInvalidNameError;
- static const std::wstring kInvalidDescriptionError;
- static const std::wstring kInvalidContentScriptsListError;
- static const std::wstring kInvalidContentScriptError;
+ static const wchar_t* kInvalidFormatVersionError;
+ static const wchar_t* kInvalidManifestError;
+ static const wchar_t* kInvalidIdError;
+ static const wchar_t* kInvalidNameError;
+ static const wchar_t* kInvalidDescriptionError;
+ static const wchar_t* kInvalidContentScriptsListError;
+ static const wchar_t* kInvalidContentScriptError;
// A human-readable ID for the extension. The convention is to use something
// like 'com.example.myextension', but this is not currently enforced. An
@@ -68,4 +73,4 @@ class Extension {
DISALLOW_COPY_AND_ASSIGN(Extension);
};
-#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_H__
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_H_
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
new file mode 100644
index 0000000..67d508e
--- /dev/null
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2006-2008 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/extensions_service.h"
+
+#include "base/file_util.h"
+#include "base/values.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/common/json_value_serializer.h"
+
+// ExtensionsService
+
+const FilePath::CharType* ExtensionsService::kInstallDirectoryName =
+ FILE_PATH_LITERAL("Extensions");
+
+ExtensionsService::ExtensionsService(const FilePath& profile_directory)
+ : message_loop_(MessageLoop::current()),
+ backend_(new ExtensionsServiceBackend),
+ install_directory_(profile_directory.Append(kInstallDirectoryName)) {
+}
+
+ExtensionsService::~ExtensionsService() {
+ for (ExtensionList::iterator iter = extensions_.begin();
+ iter != extensions_.end(); ++iter) {
+ delete *iter;
+ }
+}
+
+bool ExtensionsService::Init() {
+ // TODO(aa): This message loop should probably come from a backend
+ // interface, similar to how the message loop for the frontend comes
+ // from the frontend interface.
+ g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::LoadExtensionsFromDirectory,
+ install_directory_,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
+ // TODO(aa): Load extensions from other registered directories.
+
+ return true;
+}
+
+MessageLoop* ExtensionsService::GetMessageLoop() {
+ return message_loop_;
+}
+
+void ExtensionsService::OnExtensionsLoadedFromDirectory(
+ ExtensionList* extensions) {
+ extensions_.assign(extensions->begin(), extensions->end());
+ delete extensions;
+
+ // TODO(aa): Notify extensions are loaded.
+}
+
+void ExtensionsService::OnExtensionLoadError(const std::wstring& error) {
+ // TODO(aa): Print the error message out somewhere better. Ideally we would
+ // use the JavaScript console I think, but that is complicated since these
+ // errors are not related to any particular page.
+ LOG(WARNING) << "Error loading extension: " << error;
+}
+
+
+// ExtensionsServicesBackend
+
+bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
+ const FilePath& path,
+ scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
+ // Find all child directories in the install directory and load their
+ // manifests. Post errors and results to the frontend.
+ scoped_ptr<ExtensionList> extensions(new ExtensionList);
+ file_util::FileEnumerator enumerator(path.ToWStringHack(),
+ false, // not recursive
+ file_util::FileEnumerator::DIRECTORIES);
+ for (std::wstring child_path = enumerator.Next(); !child_path.empty();
+ child_path = enumerator.Next()) {
+ FilePath manifest_path = FilePath::FromWStringHack(child_path).Append(
+ Extension::kManifestFilename);
+ if (!file_util::PathExists(manifest_path)) {
+ ReportExtensionLoadError(frontend.get(),
+ Extension::kInvalidManifestError);
+ continue;
+ }
+
+ JSONFileValueSerializer serializer(manifest_path.ToWStringHack());
+ Value* root = NULL;
+ if (!serializer.Deserialize(&root)) {
+ ReportExtensionLoadError(frontend.get(),
+ Extension::kInvalidManifestError);
+ continue;
+ }
+
+ if (!root->IsType(Value::TYPE_DICTIONARY)) {
+ ReportExtensionLoadError(frontend.get(),
+ Extension::kInvalidManifestError);
+ continue;
+ }
+
+ scoped_ptr<Extension> extension(new Extension());
+ std::wstring error;
+ if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root),
+ &error)) {
+ ReportExtensionLoadError(frontend.get(),
+ Extension::kInvalidManifestError);
+ continue;
+ }
+
+ extensions->push_back(extension.release());
+ }
+
+ ReportExtensionsLoaded(frontend.get(), extensions.release());
+ return true;
+}
+
+void ExtensionsServiceBackend::ReportExtensionLoadError(
+ ExtensionsServiceFrontendInterface *frontend, const std::wstring &error) {
+ frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
+ error));
+}
+
+void ExtensionsServiceBackend::ReportExtensionsLoaded(
+ ExtensionsServiceFrontendInterface *frontend, ExtensionList* extensions) {
+ frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
+ frontend,
+ &ExtensionsServiceFrontendInterface::OnExtensionsLoadedFromDirectory,
+ extensions));
+}
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
new file mode 100644
index 0000000..17f25b7
--- /dev/null
+++ b/chrome/browser/extensions/extensions_service.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2006-2008 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_EXTENSIONS_SERVICE_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSIONS_SERVICE_H_
+
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "chrome/browser/extensions/extension.h"
+
+typedef std::vector<Extension*> ExtensionList;
+class ExtensionsServiceBackend;
+
+// Interface for the frontend to implement. Typically, this will be
+// ExtensionsService, but it can also be a test harness.
+class ExtensionsServiceFrontendInterface
+ : public base::RefCountedThreadSafe<ExtensionsServiceFrontendInterface> {
+ public:
+ virtual ~ExtensionsServiceFrontendInterface(){}
+
+ // The message loop to invoke the frontend's methods on.
+ virtual MessageLoop* GetMessageLoop() = 0;
+
+ // Called when loading an extension fails.
+ virtual void OnExtensionLoadError(const std::wstring& message) = 0;
+
+ // Called with results from LoadExtensionsFromDirectory(). The frontend
+ // takes ownership of the list.
+ virtual void OnExtensionsLoadedFromDirectory(ExtensionList* extensions) = 0;
+};
+
+
+// Manages installed and running Chromium extensions.
+class ExtensionsService : public ExtensionsServiceFrontendInterface {
+ public:
+ ExtensionsService(const FilePath& profile_directory);
+ ~ExtensionsService();
+
+ // Gets the list of currently installed extensions.
+ const ExtensionList* extensions() const {
+ return &extensions_;
+ }
+
+ // Initialize and start all installed extensions.
+ bool Init();
+
+ // ExtensionsServiceFrontendInterface
+ virtual MessageLoop* GetMessageLoop();
+ virtual void OnExtensionLoadError(const std::wstring& message);
+ virtual void OnExtensionsLoadedFromDirectory(ExtensionList* extensions);
+
+ private:
+ // The name of the directory inside the profile where extensions are
+ // installed to.
+ static const FilePath::CharType* kInstallDirectoryName;
+
+ // The message loop for the thread the ExtensionsService is running on.
+ MessageLoop* message_loop_;
+
+ // The backend that will do IO on behalf of this instance.
+ scoped_refptr<ExtensionsServiceBackend> backend_;
+
+ // The current list of installed extensions.
+ ExtensionList extensions_;
+
+ // The full path to the directory where extensions are installed.
+ FilePath install_directory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionsService);
+};
+
+// Implements IO for the ExtensionsService.
+// TODO(aa): Extract an interface out of this for testing the frontend, once the
+// frontend has significant logic to test.
+class ExtensionsServiceBackend
+ : public base::RefCountedThreadSafe<ExtensionsServiceBackend> {
+ public:
+ ExtensionsServiceBackend(){};
+
+ // Loads extensions from a directory. The extensions are assumed to be
+ // unpacked in directories that are direct children of the specified path.
+ // Errors are reported through OnExtensionLoadError(). On completion,
+ // OnExtensionsLoadedFromDirectory() is called with any successfully loaded
+ // extensions.
+ bool LoadExtensionsFromDirectory(
+ const FilePath &path,
+ scoped_refptr<ExtensionsServiceFrontendInterface> frontend);
+
+ private:
+ // Notify a frontend that there was an error loading an extension.
+ void ReportExtensionLoadError(ExtensionsServiceFrontendInterface* frontend,
+ const std::wstring& error);
+
+ // Notify a frontend that extensions were loaded.
+ void ReportExtensionsLoaded(ExtensionsServiceFrontendInterface* frontend,
+ ExtensionList* extensions);
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceBackend);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSIONS_SERVICE_H_
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
new file mode 100644
index 0000000..22e891d
--- /dev/null
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2006-2008 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 <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/json_value_serializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ExtensionsServiceTest : public testing::Test {
+};
+
+
+// A mock implementation of ExtensionsServiceFrontendInterface for testing the
+// backend.
+class ExtensionsServiceTestFrontend
+ : public ExtensionsServiceFrontendInterface {
+ public:
+ std::vector<std::wstring>* errors() {
+ return &errors_;
+ }
+
+ ExtensionList* extensions() {
+ return extensions_.get();
+ }
+
+ // ExtensionsServiceFrontendInterface
+ virtual MessageLoop* GetMessageLoop() {
+ return &message_loop_;
+ }
+
+ virtual void OnExtensionLoadError(const std::wstring& message) {
+ errors_.push_back(message);
+ }
+
+ virtual void OnExtensionsLoadedFromDirectory(ExtensionList* extensions) {
+ extensions_.reset(extensions);
+ }
+
+ private:
+ MessageLoop message_loop_;
+ scoped_ptr<ExtensionList> extensions_;
+ std::vector<std::wstring> errors_;
+};
+
+
+// Test loading extensions from the profile directory.
+TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectory) {
+#if defined(OS_WIN)
+ std::wstring extensions_dir;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_dir));
+ FilePath manifest_path = FilePath::FromWStringHack(extensions_dir).Append(
+ FILE_PATH_LITERAL("extensions"));
+
+ scoped_refptr<ExtensionsServiceBackend> backend(new ExtensionsServiceBackend);
+ scoped_refptr<ExtensionsServiceTestFrontend> frontend(
+ new ExtensionsServiceTestFrontend);
+
+ std::vector<Extension*> extensions;
+ EXPECT_TRUE(backend->LoadExtensionsFromDirectory(manifest_path,
+ scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())));
+ frontend->GetMessageLoop()->RunAllPending();
+
+ // Note: There can be more errors if there are extra directories, like .svn
+ // directories.
+ EXPECT_TRUE(frontend->errors()->size() >= 2u);
+ EXPECT_EQ(Extension::kInvalidManifestError, frontend->errors()->at(0));
+ EXPECT_EQ(Extension::kInvalidManifestError, frontend->errors()->at(1));
+ EXPECT_EQ(2u, frontend->extensions()->size());
+
+ EXPECT_EQ(std::wstring(L"com.google.myextension1"),
+ frontend->extensions()->at(0)->id());
+ EXPECT_EQ(std::wstring(L"My extension 1"),
+ frontend->extensions()->at(0)->name());
+ EXPECT_EQ(std::wstring(L"The first extension that I made."),
+ frontend->extensions()->at(0)->description());
+ EXPECT_EQ(2u, frontend->extensions()->at(0)->content_scripts().size());
+ EXPECT_EQ(std::wstring(L"script1.user.js"),
+ frontend->extensions()->at(0)->content_scripts().at(0));
+ EXPECT_EQ(std::wstring(L"script2.user.js"),
+ frontend->extensions()->at(0)->content_scripts().at(1));
+
+ EXPECT_EQ(std::wstring(L"com.google.myextension2"),
+ frontend->extensions()->at(1)->id());
+ EXPECT_EQ(std::wstring(L"My extension 2"),
+ frontend->extensions()->at(1)->name());
+ EXPECT_EQ(std::wstring(L""),
+ frontend->extensions()->at(1)->description());
+ EXPECT_EQ(0u, frontend->extensions()->at(1)->content_scripts().size());
+#endif
+};