Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / chrome / renderer / extensions / webstore_bindings.cc
blobc364d2ca0de693cea94c4700fa6ec8cd9f618e1c
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/api/webstore/webstore_api_constants.h"
9 #include "chrome/common/extensions/chrome_extension_messages.h"
10 #include "components/crx_file/id_util.h"
11 #include "content/public/renderer/render_view.h"
12 #include "extensions/common/extension.h"
13 #include "extensions/common/extension_urls.h"
14 #include "extensions/renderer/script_context.h"
15 #include "third_party/WebKit/public/web/WebDocument.h"
16 #include "third_party/WebKit/public/web/WebElement.h"
17 #include "third_party/WebKit/public/web/WebNode.h"
18 #include "third_party/WebKit/public/web/WebNodeList.h"
19 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
20 #include "url/gurl.h"
21 #include "v8/include/v8.h"
23 using blink::WebDocument;
24 using blink::WebElement;
25 using blink::WebFrame;
26 using blink::WebNode;
27 using blink::WebNodeList;
28 using blink::WebUserGestureIndicator;
30 namespace extensions {
32 namespace {
34 const char kWebstoreLinkRelation[] = "chrome-webstore-item";
36 const char kNotInTopFrameError[] =
37 "Chrome Web Store installations can only be started by the top frame.";
38 const char kNotUserGestureError[] =
39 "Chrome Web Store installations can only be initated by a user gesture.";
40 const char kNoWebstoreItemLinkFoundError[] =
41 "No Chrome Web Store item link found.";
42 const char kInvalidWebstoreItemUrlError[] =
43 "Invalid Chrome Web Store item URL.";
45 // chrome.webstore.install() calls generate an install ID so that the install's
46 // callbacks may be fired when the browser notifies us of install completion
47 // (successful or not) via OnInlineWebstoreInstallResponse.
48 int g_next_install_id = 0;
50 } // anonymous namespace
52 WebstoreBindings::WebstoreBindings(ScriptContext* context)
53 : ObjectBackedNativeHandler(context) {
54 RouteFunction("Install",
55 base::Bind(&WebstoreBindings::Install, base::Unretained(this)));
58 void WebstoreBindings::Install(
59 const v8::FunctionCallbackInfo<v8::Value>& args) {
60 content::RenderView* render_view = context()->GetRenderView();
61 if (!render_view)
62 return;
64 // The first two arguments indicate whether or not there are install stage
65 // or download progress listeners.
66 int listener_mask = 0;
67 CHECK(args[0]->IsBoolean());
68 if (args[0]->BooleanValue())
69 listener_mask |= api::webstore::INSTALL_STAGE_LISTENER;
70 CHECK(args[1]->IsBoolean());
71 if (args[1]->BooleanValue())
72 listener_mask |= api::webstore::DOWNLOAD_PROGRESS_LISTENER;
74 std::string preferred_store_link_url;
75 if (!args[2]->IsUndefined()) {
76 CHECK(args[2]->IsString());
77 preferred_store_link_url = std::string(*v8::String::Utf8Value(args[2]));
80 std::string webstore_item_id;
81 std::string error;
82 WebFrame* frame = context()->web_frame();
84 if (!GetWebstoreItemIdFromFrame(
85 frame, preferred_store_link_url, &webstore_item_id, &error)) {
86 args.GetIsolate()->ThrowException(
87 v8::String::NewFromUtf8(args.GetIsolate(), error.c_str()));
88 return;
91 int install_id = g_next_install_id++;
93 Send(new ExtensionHostMsg_InlineWebstoreInstall(render_view->GetRoutingID(),
94 install_id,
95 GetRoutingID(),
96 webstore_item_id,
97 frame->document().url(),
98 listener_mask));
100 args.GetReturnValue().Set(static_cast<int32_t>(install_id));
103 // static
104 bool WebstoreBindings::GetWebstoreItemIdFromFrame(
105 WebFrame* frame,
106 const std::string& preferred_store_link_url,
107 std::string* webstore_item_id,
108 std::string* error) {
109 if (frame != frame->top()) {
110 *error = kNotInTopFrameError;
111 return false;
114 if (!WebUserGestureIndicator::isProcessingUserGesture()) {
115 *error = kNotUserGestureError;
116 return false;
119 WebDocument document = frame->document();
120 if (document.isNull()) {
121 *error = kNoWebstoreItemLinkFoundError;
122 return false;
125 WebElement head = document.head();
126 if (head.isNull()) {
127 *error = kNoWebstoreItemLinkFoundError;
128 return false;
131 GURL webstore_base_url =
132 GURL(extension_urls::GetWebstoreItemDetailURLPrefix());
133 WebNodeList children = head.childNodes();
134 for (unsigned i = 0; i < children.length(); ++i) {
135 WebNode child = children.item(i);
136 if (!child.isElementNode())
137 continue;
138 WebElement elem = child.to<WebElement>();
140 if (!elem.hasHTMLTagName("link") || !elem.hasAttribute("rel") ||
141 !elem.hasAttribute("href"))
142 continue;
144 std::string rel = elem.getAttribute("rel").utf8();
145 if (!LowerCaseEqualsASCII(rel, kWebstoreLinkRelation))
146 continue;
148 std::string webstore_url_string(elem.getAttribute("href").utf8());
150 if (!preferred_store_link_url.empty() &&
151 preferred_store_link_url != webstore_url_string) {
152 continue;
155 GURL webstore_url = GURL(webstore_url_string);
156 if (!webstore_url.is_valid()) {
157 *error = kInvalidWebstoreItemUrlError;
158 return false;
161 if (webstore_url.scheme() != webstore_base_url.scheme() ||
162 webstore_url.host() != webstore_base_url.host() ||
163 !StartsWithASCII(
164 webstore_url.path(), webstore_base_url.path(), true)) {
165 *error = kInvalidWebstoreItemUrlError;
166 return false;
169 std::string candidate_webstore_item_id = webstore_url.path().substr(
170 webstore_base_url.path().length());
171 if (!crx_file::id_util::IdIsValid(candidate_webstore_item_id)) {
172 *error = kInvalidWebstoreItemUrlError;
173 return false;
176 std::string reconstructed_webstore_item_url_string =
177 extension_urls::GetWebstoreItemDetailURLPrefix() +
178 candidate_webstore_item_id;
179 if (reconstructed_webstore_item_url_string != webstore_url_string) {
180 *error = kInvalidWebstoreItemUrlError;
181 return false;
184 *webstore_item_id = candidate_webstore_item_id;
185 return true;
188 *error = kNoWebstoreItemLinkFoundError;
189 return false;
192 bool WebstoreBindings::OnMessageReceived(const IPC::Message& message) {
193 IPC_BEGIN_MESSAGE_MAP(WebstoreBindings, message)
194 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineWebstoreInstallResponse,
195 OnInlineWebstoreInstallResponse)
196 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineInstallStageChanged,
197 OnInlineInstallStageChanged)
198 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineInstallDownloadProgress,
199 OnInlineInstallDownloadProgress)
200 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
201 IPC_END_MESSAGE_MAP()
202 return true;
205 void WebstoreBindings::OnInlineWebstoreInstallResponse(
206 int install_id,
207 bool success,
208 const std::string& error,
209 webstore_install::Result result) {
210 v8::Isolate* isolate = context()->isolate();
211 v8::HandleScope handle_scope(isolate);
212 v8::Context::Scope context_scope(context()->v8_context());
213 v8::Handle<v8::Value> argv[] = {
214 v8::Integer::New(isolate, install_id),
215 v8::Boolean::New(isolate, success),
216 v8::String::NewFromUtf8(isolate, error.c_str()),
217 v8::String::NewFromUtf8(
218 isolate, api::webstore::kInstallResultCodes[static_cast<int>(result)])
220 context()->module_system()->CallModuleMethod(
221 "webstore", "onInstallResponse", arraysize(argv), argv);
224 void WebstoreBindings::OnInlineInstallStageChanged(int stage) {
225 const char* stage_string = NULL;
226 api::webstore::InstallStage install_stage =
227 static_cast<api::webstore::InstallStage>(stage);
228 switch (install_stage) {
229 case api::webstore::INSTALL_STAGE_DOWNLOADING:
230 stage_string = api::webstore::kInstallStageDownloading;
231 break;
232 case api::webstore::INSTALL_STAGE_INSTALLING:
233 stage_string = api::webstore::kInstallStageInstalling;
234 break;
236 v8::Isolate* isolate = context()->isolate();
237 v8::HandleScope handle_scope(isolate);
238 v8::Context::Scope context_scope(context()->v8_context());
239 v8::Handle<v8::Value> argv[] = {
240 v8::String::NewFromUtf8(isolate, stage_string)};
241 context()->module_system()->CallModuleMethod(
242 "webstore", "onInstallStageChanged", arraysize(argv), argv);
245 void WebstoreBindings::OnInlineInstallDownloadProgress(int percent_downloaded) {
246 v8::Isolate* isolate = context()->isolate();
247 v8::HandleScope handle_scope(isolate);
248 v8::Context::Scope context_scope(context()->v8_context());
249 v8::Handle<v8::Value> argv[] = {
250 v8::Number::New(isolate, percent_downloaded / 100.0)};
251 context()->module_system()->CallModuleMethod(
252 "webstore", "onDownloadProgress", arraysize(argv), argv);
255 } // namespace extensions