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(),