Implement extension:// protocol.
Review URL: http://codereview.chromium.org/15010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7462 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension.h b/chrome/browser/extensions/extension.h
index 691a0b7..ac6c671 100644
--- a/chrome/browser/extensions/extension.h
+++ b/chrome/browser/extensions/extension.h
@@ -16,6 +16,7 @@
class Extension {
public:
Extension(){};
+ Extension(const FilePath& path) : path_(path) {};
// The format for extension manifests that this code understands.
static const int kExpectedFormatVersion = 1;
@@ -39,6 +40,9 @@
static const char* kInvalidContentScriptsListError;
static const char* kInvalidContentScriptError;
+ // The path to the folder the extension is stored in.
+ const FilePath& path() const { return path_; }
+
// A human-readable ID for the extension. The convention is to use something
// like 'com.example.myextension', but this is not currently enforced. An
// extension's ID is used in things like directory structures and URLs, and
@@ -65,9 +69,19 @@
void CopyToValue(DictionaryValue* value);
private:
+ // The path to the directory the extension is stored in.
+ FilePath path_;
+
+ // The extension's ID.
std::string id_;
+
+ // The extension's human-readable name.
std::string name_;
+
+ // An optional description for the extension.
std::string description_;
+
+ // Paths to the content scripts the extension contains.
std::vector<std::string> content_scripts_;
DISALLOW_COPY_AND_ASSIGN(Extension);
diff --git a/chrome/browser/extensions/extension_protocol.cc b/chrome/browser/extensions/extension_protocol.cc
new file mode 100644
index 0000000..cb1ee09
--- /dev/null
+++ b/chrome/browser/extensions/extension_protocol.cc
@@ -0,0 +1,78 @@
+// 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/extension_protocol.h"
+
+#include "base/string_util.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "googleurl/src/url_util.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request_file_job.h"
+
+static const char kExtensionURLScheme[] = "chrome-extension";
+
+FilePath GetPathForExtensionResource(const FilePath& extension_path,
+ const std::string& url_path) {
+ DCHECK(extension_path.IsAbsolute());
+ DCHECK(url_path.length() > 0 && url_path[0] == '/');
+
+ // Build up a file:// URL and convert that back to a FilePath. This avoids
+ // URL encoding and path separator issues.
+
+ // Convert the extension's root to a file:// URL.
+ GURL extension_url = net::FilePathToFileURL(extension_path);
+ if (!extension_url.is_valid())
+ return FilePath();
+
+ // Append the requested path.
+ GURL::Replacements replacements;
+ std::string new_path(extension_url.path());
+ new_path += url_path;
+ replacements.SetPathStr(new_path);
+ GURL file_url = extension_url.ReplaceComponents(replacements);
+ if (!file_url.is_valid())
+ return FilePath();
+
+ // Convert the result back to a FilePath.
+ FilePath ret_val;
+ if (!net::FileURLToFilePath(file_url, &ret_val))
+ return FilePath();
+
+ if (!file_util::AbsolutePath(&ret_val))
+ return FilePath();
+
+ // Double-check that the path we ended up with is actually inside the
+ // extension root.
+ if (!extension_path.Contains(ret_val))
+ return FilePath();
+
+ return ret_val;
+}
+
+// Creates a URLRequestJob instance for an extension URL. This is the factory
+// function that is registered with URLRequest.
+static URLRequestJob* CreateURLRequestJob(URLRequest* request,
+ const std::string& scheme) {
+ ChromeURLRequestContext* context =
+ static_cast<ChromeURLRequestContext*>(request->context());
+
+ FilePath extension_path = context->GetPathForExtension(request->url().host());
+ if (extension_path.value().empty())
+ return NULL;
+
+ FilePath path = GetPathForExtensionResource(extension_path,
+ request->url().path());
+ if (path.value().empty())
+ return NULL;
+
+ return new URLRequestFileJob(request, path);
+}
+
+void RegisterExtensionProtocol() {
+ // Being a standard scheme allows us to resolve relative paths
+ url_util::AddStandardScheme(kExtensionURLScheme);
+
+ URLRequest::RegisterProtocolFactory(kExtensionURLScheme,
+ &CreateURLRequestJob);
+}
diff --git a/chrome/browser/extensions/extension_protocol.h b/chrome/browser/extensions/extension_protocol.h
new file mode 100644
index 0000000..54254db
--- /dev/null
+++ b/chrome/browser/extensions/extension_protocol.h
@@ -0,0 +1,14 @@
+// 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 "base/file_path.h"
+
+// Gets a FilePath for a resource inside an extension. |extension_path| is the
+// full path to the extension directory. |resource_path| is the path to the
+// resource from the extension root, including the leading '/'.
+FilePath GetPathForExtensionResource(const FilePath& extension_path,
+ const std::string& resource_path);
+
+// Registers support for the extension URL scheme.
+void RegisterExtensionProtocol();
diff --git a/chrome/browser/extensions/extension_protocol_unittest.cc b/chrome/browser/extensions/extension_protocol_unittest.cc
new file mode 100644
index 0000000..1d2b4f2
--- /dev/null
+++ b/chrome/browser/extensions/extension_protocol_unittest.cc
@@ -0,0 +1,39 @@
+// 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/extension_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ExtensionProtocolTest : public testing::Test {
+};
+
+TEST(ExtensionProtocolTest, GetPathForExtensionResource) {
+#if defined(OS_WIN)
+ FilePath extension_path(FILE_PATH_LITERAL("C:\\myextension"));
+ EXPECT_EQ(std::wstring(L"C:\\myextension\\foo\\bar.gif"),
+ GetPathForExtensionResource(extension_path, "/foo/bar.gif").value());
+ EXPECT_EQ(std::wstring(L"C:\\myextension\\"),
+ GetPathForExtensionResource(extension_path, "/").value());
+ // TODO(aa): This one is a bit weird, but is what net::FileURLToFilePath()
+ // returns for this input. Investigate adding more validation.
+ EXPECT_EQ(std::wstring(L"C:\\myextension\\c:\\foo.gif"),
+ GetPathForExtensionResource(extension_path, "/c:/foo.gif").value());
+ EXPECT_EQ(std::wstring(L"C:\\myextension\\foo.gif"),
+ GetPathForExtensionResource(extension_path, "//foo.gif").value());
+ EXPECT_EQ(std::wstring(L""),
+ GetPathForExtensionResource(extension_path, "/../foo.gif").value());
+#else
+ FilePath extension_path(FILE_PATH_LITERAL("/myextension"));
+ EXPECT_EQ(std::wstring("/myextension/foo/bar.gif"),
+ GetPathForExtensionResource(extension_path, "/foo/bar.gif").value());
+ EXPECT_EQ(std::wstring("/myextension/"),
+ GetPathForExtensionResource(extension_path, "/").value());
+ EXPECT_EQ(std::wstring("/myextension/foo.gif"),
+ GetPathForExtensionResource(extension_path, "//foo.gif").value());
+ EXPECT_EQ(std::wstring(""),
+ GetPathForExtensionResource(extension_path, "/../foo.gif").value());
+#endif
+
+
+}
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 0245ce6..cd0397c 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -10,6 +10,7 @@
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/json_value_serializer.h"
+#include "chrome/common/notification_service.h"
// ExtensionsService
@@ -51,9 +52,12 @@
ExtensionList* new_extensions) {
extensions_.insert(extensions_.end(), new_extensions->begin(),
new_extensions->end());
- delete new_extensions;
- // TODO(aa): Notify extensions are loaded.
+ NotificationService::current()->Notify(NOTIFY_EXTENSIONS_LOADED,
+ NotificationService::AllSources(),
+ Details<ExtensionList>(new_extensions));
+
+ delete new_extensions;
}
void ExtensionsService::OnExtensionLoadError(const std::string& error) {
@@ -99,7 +103,7 @@
continue;
}
- scoped_ptr<Extension> extension(new Extension());
+ scoped_ptr<Extension> extension(new Extension(child_path));
if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root),
&error)) {
ReportExtensionLoadError(frontend.get(), child_path.ToWStringHack(),