Move extension_messages.h to extensions/common.
[chromium-blink-merge.git] / chrome / renderer / extensions / webstore_bindings.cc
blob21f3b22d8b183ff74a405b0f43d7b5a64d9d40f6
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/extensions/webstore_bindings.h"
7 #include "base/strings/string_util.h"
8 #include "chrome/common/extensions/extension_constants.h"
9 #include "chrome/renderer/extensions/chrome_v8_context.h"
10 #include "content/public/renderer/render_view.h"
11 #include "extensions/common/extension.h"
12 #include "extensions/common/extension_messages.h"
13 #include "grit/renderer_resources.h"
14 #include "third_party/WebKit/public/web/WebDocument.h"
15 #include "third_party/WebKit/public/web/WebElement.h"
16 #include "third_party/WebKit/public/web/WebNode.h"
17 #include "third_party/WebKit/public/web/WebNodeList.h"
18 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
19 #include "url/gurl.h"
20 #include "v8/include/v8.h"
22 using blink::WebDocument;
23 using blink::WebElement;
24 using blink::WebFrame;
25 using blink::WebNode;
26 using blink::WebNodeList;
27 using blink::WebUserGestureIndicator;
29 namespace extensions {
31 namespace {
33 const char kWebstoreLinkRelation[] = "chrome-webstore-item";
35 const char kPreferredStoreLinkUrlNotAString[] =
36 "The Chrome Web Store item link URL parameter must be a string.";
37 const char kSuccessCallbackNotAFunctionError[] =
38 "The success callback parameter must be a function.";
39 const char kFailureCallbackNotAFunctionError[] =
40 "The failure callback parameter must be a function.";
41 const char kNotInTopFrameError[] =
42 "Chrome Web Store installations can only be started by the top frame.";
43 const char kNotUserGestureError[] =
44 "Chrome Web Store installations can only be initated by a user gesture.";
45 const char kNoWebstoreItemLinkFoundError[] =
46 "No Chrome Web Store item link found.";
47 const char kInvalidWebstoreItemUrlError[] =
48 "Invalid Chrome Web Store item URL.";
50 // chrome.webstore.install() calls generate an install ID so that the install's
51 // callbacks may be fired when the browser notifies us of install completion
52 // (successful or not) via OnInlineWebstoreInstallResponse.
53 int g_next_install_id = 0;
55 } // anonymous namespace
57 WebstoreBindings::WebstoreBindings(Dispatcher* dispatcher,
58 ChromeV8Context* context)
59 : ChromeV8Extension(dispatcher, context),
60 ChromeV8ExtensionHandler(context) {
61 RouteFunction("Install",
62 base::Bind(&WebstoreBindings::Install, base::Unretained(this)));
65 void WebstoreBindings::Install(
66 const v8::FunctionCallbackInfo<v8::Value>& args) {
67 WebFrame* frame = WebFrame::frameForContext(context()->v8_context());
68 if (!frame || !frame->view())
69 return;
71 content::RenderView* render_view =
72 content::RenderView::FromWebView(frame->view());
73 if (!render_view)
74 return;
76 std::string preferred_store_link_url;
77 if (!args[0]->IsUndefined()) {
78 if (args[0]->IsString()) {
79 preferred_store_link_url = std::string(*v8::String::Utf8Value(args[0]));
80 } else {
81 args.GetIsolate()->ThrowException(v8::String::NewFromUtf8(
82 args.GetIsolate(), kPreferredStoreLinkUrlNotAString));
83 return;
87 std::string webstore_item_id;
88 std::string error;
89 if (!GetWebstoreItemIdFromFrame(
90 frame, preferred_store_link_url, &webstore_item_id, &error)) {
91 args.GetIsolate()->ThrowException(
92 v8::String::NewFromUtf8(args.GetIsolate(), error.c_str()));
93 return;
96 int install_id = g_next_install_id++;
97 if (!args[1]->IsUndefined() && !args[1]->IsFunction()) {
98 args.GetIsolate()->ThrowException(v8::String::NewFromUtf8(
99 args.GetIsolate(), kSuccessCallbackNotAFunctionError));
100 return;
103 if (!args[2]->IsUndefined() && !args[2]->IsFunction()) {
104 args.GetIsolate()->ThrowException(v8::String::NewFromUtf8(
105 args.GetIsolate(), kFailureCallbackNotAFunctionError));
106 return;
109 Send(new ExtensionHostMsg_InlineWebstoreInstall(
110 render_view->GetRoutingID(),
111 install_id,
112 GetRoutingID(),
113 webstore_item_id,
114 frame->document().url()));
116 args.GetReturnValue().Set(static_cast<int32_t>(install_id));
119 // static
120 bool WebstoreBindings::GetWebstoreItemIdFromFrame(
121 WebFrame* frame, const std::string& preferred_store_link_url,
122 std::string* webstore_item_id, std::string* error) {
123 if (frame != frame->top()) {
124 *error = kNotInTopFrameError;
125 return false;
128 if (!WebUserGestureIndicator::isProcessingUserGesture()) {
129 *error = kNotUserGestureError;
130 return false;
133 WebDocument document = frame->document();
134 if (document.isNull()) {
135 *error = kNoWebstoreItemLinkFoundError;
136 return false;
139 WebElement head = document.head();
140 if (head.isNull()) {
141 *error = kNoWebstoreItemLinkFoundError;
142 return false;
145 GURL webstore_base_url =
146 GURL(extension_urls::GetWebstoreItemDetailURLPrefix());
147 WebNodeList children = head.childNodes();
148 for (unsigned i = 0; i < children.length(); ++i) {
149 WebNode child = children.item(i);
150 if (!child.isElementNode())
151 continue;
152 WebElement elem = child.to<WebElement>();
154 if (!elem.hasTagName("link") || !elem.hasAttribute("rel") ||
155 !elem.hasAttribute("href"))
156 continue;
158 std::string rel = elem.getAttribute("rel").utf8();
159 if (!LowerCaseEqualsASCII(rel, kWebstoreLinkRelation))
160 continue;
162 std::string webstore_url_string(elem.getAttribute("href").utf8());
164 if (!preferred_store_link_url.empty() &&
165 preferred_store_link_url != webstore_url_string) {
166 continue;
169 GURL webstore_url = GURL(webstore_url_string);
170 if (!webstore_url.is_valid()) {
171 *error = kInvalidWebstoreItemUrlError;
172 return false;
175 if (webstore_url.scheme() != webstore_base_url.scheme() ||
176 webstore_url.host() != webstore_base_url.host() ||
177 !StartsWithASCII(
178 webstore_url.path(), webstore_base_url.path(), true)) {
179 *error = kInvalidWebstoreItemUrlError;
180 return false;
183 std::string candidate_webstore_item_id = webstore_url.path().substr(
184 webstore_base_url.path().length());
185 if (!extensions::Extension::IdIsValid(candidate_webstore_item_id)) {
186 *error = kInvalidWebstoreItemUrlError;
187 return false;
190 std::string reconstructed_webstore_item_url_string =
191 extension_urls::GetWebstoreItemDetailURLPrefix() +
192 candidate_webstore_item_id;
193 if (reconstructed_webstore_item_url_string != webstore_url_string) {
194 *error = kInvalidWebstoreItemUrlError;
195 return false;
198 *webstore_item_id = candidate_webstore_item_id;
199 return true;
202 *error = kNoWebstoreItemLinkFoundError;
203 return false;
206 bool WebstoreBindings::OnMessageReceived(const IPC::Message& message) {
207 IPC_BEGIN_MESSAGE_MAP(WebstoreBindings, message)
208 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineWebstoreInstallResponse,
209 OnInlineWebstoreInstallResponse)
210 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
211 IPC_END_MESSAGE_MAP()
212 return true;
215 void WebstoreBindings::OnInlineWebstoreInstallResponse(
216 int install_id,
217 bool success,
218 const std::string& error) {
219 v8::Isolate* isolate = context()->isolate();
220 v8::HandleScope handle_scope(isolate);
221 v8::Context::Scope context_scope(context()->v8_context());
222 v8::Handle<v8::Value> argv[] = {
223 v8::Integer::New(isolate, install_id),
224 v8::Boolean::New(isolate, success),
225 v8::String::NewFromUtf8(isolate, error.c_str())
227 context()->module_system()->CallModuleMethod(
228 "webstore", "onInstallResponse", arraysize(argv), argv);
231 } // namespace extensions