Index: chrome/renderer/extensions/chrome_webstore_bindings.cc |
diff --git a/chrome/renderer/extensions/chrome_webstore_bindings.cc b/chrome/renderer/extensions/chrome_webstore_bindings.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9c58d15c1b8c4519e188af813f63cfcaa5dfef11 |
--- /dev/null |
+++ b/chrome/renderer/extensions/chrome_webstore_bindings.cc |
@@ -0,0 +1,168 @@ |
+// 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/renderer/extensions/chrome_webstore_bindings.h" |
+ |
+#include "base/string_util.h" |
+#include "chrome/common/extensions/extension.h" |
+#include "chrome/renderer/extensions/bindings_utils.h" |
+#include "chrome/renderer/extensions/extension_helper.h" |
+#include "content/renderer/render_view.h" |
+#include "googleurl/src/gurl.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeList.h" |
+#include "v8/include/v8.h" |
+ |
+using WebKit::WebDocument; |
+using WebKit::WebElement; |
+using WebKit::WebFrame; |
+using WebKit::WebNode; |
+using WebKit::WebNodeList; |
+ |
+const char kWebstoreLinkRelation[] = "chrome-webstore-item"; |
+ |
+const char kNotInTopFrameError[] = |
+ "Chrome Web Store installations can only be started by the top frame."; |
+const char kNotUserGestureError[] = |
+ "Chrome Web Store installations can only be initated by a user gesture."; |
+const char kNoWebstoreItemLinkFoundError[] = |
+ "No Chrome Web Store item link found."; |
+const char kInvalidWebstoreItemUrlError[] = |
+ "Invalid Chrome Web Store item URL."; |
+ |
+namespace extensions_v8 { |
+ |
+static const char* const kWebstoreExtensionName = "v8/ChromeWebstore"; |
+ |
+class ChromeWebstoreExtensionWrapper : public v8::Extension { |
+ public: |
+ ChromeWebstoreExtensionWrapper() : |
+ v8::Extension( |
+ kWebstoreExtensionName, |
+ "var chrome;" |
+ "if (!chrome)" |
+ " chrome = {};" |
+ "if (!chrome.webstore) {" |
+ " chrome.webstore = new function() {" |
+ " native function Install();" |
+ " this.install = Install;" |
+ " };" |
+ "}") { |
+ } |
+ |
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
+ v8::Handle<v8::String> name) { |
+ if (name->Equals(v8::String::New("Install"))) { |
+ return v8::FunctionTemplate::New(Install); |
+ } else { |
+ return v8::Handle<v8::FunctionTemplate>(); |
+ } |
+ } |
+ |
+ static v8::Handle<v8::Value> Install(const v8::Arguments& args) { |
+ WebFrame* frame = WebFrame::frameForCurrentContext(); |
+ RenderView* render_view = bindings_utils::GetRenderViewForCurrentContext(); |
+ if (frame && render_view) { |
+ std::string webstore_item_id; |
+ std::string error; |
+ if (GetWebstoreItemIdFromFrame(frame, &webstore_item_id, &error)) { |
+ ExtensionHelper* helper = ExtensionHelper::Get(render_view); |
+ helper->InlineWebstoreInstall(webstore_item_id); |
+ } else { |
+ v8::ThrowException(v8::String::New(error.c_str())); |
+ } |
+ } |
+ |
+ return v8::Undefined(); |
+ } |
+ |
+ private: |
+ // Extracts a Web Store item ID from a <link rel="chrome-webstore-item" |
+ // href="https://chrome.google.com/webstore/detail/id"> node found in the |
+ // frame. On success, true will be returned and the |webstore_item_id| |
+ // parameter will be populated with the ID. On failure, false will be returned |
+ // and |error| will be populated with the error. |
+ static bool GetWebstoreItemIdFromFrame( |
+ WebFrame* frame, std::string* webstore_item_id, std::string* error) { |
+ if (frame != frame->top()) { |
+ *error = kNotInTopFrameError; |
+ return false; |
+ } |
+ |
+ if (!frame->isProcessingUserGesture()) { |
+ *error = kNotUserGestureError; |
+ return false; |
+ } |
+ |
+ WebDocument document = frame->document(); |
+ if (document.isNull()) { |
+ *error = kNoWebstoreItemLinkFoundError; |
+ return false; |
+ } |
+ |
+ WebElement head = document.head(); |
+ if (head.isNull()) { |
+ *error = kNoWebstoreItemLinkFoundError; |
+ return false; |
+ } |
+ |
+ GURL webstore_base_url = |
+ GURL(extension_misc::GetWebstoreItemDetailURLPrefix()); |
+ WebNodeList children = head.childNodes(); |
+ for (unsigned i = 0; i < children.length(); ++i) { |
+ WebNode child = children.item(i); |
+ if (!child.isElementNode()) |
+ continue; |
+ WebElement elem = child.to<WebElement>(); |
+ |
+ if (!elem.hasTagName("link") || !elem.hasAttribute("rel") || |
+ !elem.hasAttribute("href")) |
+ continue; |
+ |
+ std::string rel = elem.getAttribute("rel").utf8(); |
+ if (!LowerCaseEqualsASCII(rel, kWebstoreLinkRelation)) |
+ continue; |
+ |
+ std::string webstore_url_string(elem.getAttribute("href").utf8()); |
+ GURL webstore_url = GURL(webstore_url_string); |
+ if (!webstore_url.is_valid()) { |
+ *error = kInvalidWebstoreItemUrlError; |
+ return false; |
+ } |
+ |
+ if (webstore_url.scheme() != webstore_base_url.scheme() || |
+ webstore_url.host() != webstore_base_url.host() || |
+ !StartsWithASCII( |
+ webstore_url.path(), webstore_base_url.path(), true)) { |
+ *error = kInvalidWebstoreItemUrlError; |
+ return false; |
+ } |
+ |
+ std::string candidate_webstore_item_id = webstore_url.path().substr( |
+ webstore_base_url.path().length()); |
+ std::string reconstructed_webstore_item_url_string = |
+ extension_misc::GetWebstoreItemDetailURLPrefix() + |
+ candidate_webstore_item_id; |
+ if (reconstructed_webstore_item_url_string != webstore_url_string) { |
+ *error = kInvalidWebstoreItemUrlError; |
+ return false; |
+ } |
+ |
+ *webstore_item_id = candidate_webstore_item_id; |
+ return true; |
+ } |
+ |
+ *error = kNoWebstoreItemLinkFoundError; |
+ return false; |
+ } |
+}; |
+ |
+v8::Extension* ChromeWebstoreExtension::Get() { |
+ return new ChromeWebstoreExtensionWrapper(); |
+} |
+ |
+} // namespace extensions_v8 |