diff options
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r-- | chrome/browser/extensions/extension.cc | 41 | ||||
-rw-r--r-- | chrome/browser/extensions/extension.h | 33 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 130 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 106 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service_unittest.cc | 98 |
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 +}; |