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 "chrome/common/extensions/extension_constants.h"
11 #include "content/public/renderer/render_view.h"
12 #include "extensions/common/extension.h"
13 #include "extensions/renderer/script_context.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"
20 #include "v8/include/v8.h"
22 using blink::WebDocument
;
23 using blink::WebElement
;
24 using blink::WebFrame
;
26 using blink::WebNodeList
;
27 using blink::WebUserGestureIndicator
;
29 namespace extensions
{
33 const char kWebstoreLinkRelation
[] = "chrome-webstore-item";
35 const char kNotInTopFrameError
[] =
36 "Chrome Web Store installations can only be started by the top frame.";
37 const char kNotUserGestureError
[] =
38 "Chrome Web Store installations can only be initated by a user gesture.";
39 const char kNoWebstoreItemLinkFoundError
[] =
40 "No Chrome Web Store item link found.";
41 const char kInvalidWebstoreItemUrlError
[] =
42 "Invalid Chrome Web Store item URL.";
44 // chrome.webstore.install() calls generate an install ID so that the install's
45 // callbacks may be fired when the browser notifies us of install completion
46 // (successful or not) via OnInlineWebstoreInstallResponse.
47 int g_next_install_id
= 0;
49 } // anonymous namespace
51 WebstoreBindings::WebstoreBindings(ScriptContext
* context
)
52 : ObjectBackedNativeHandler(context
), ChromeV8ExtensionHandler(context
) {
53 RouteFunction("Install",
54 base::Bind(&WebstoreBindings::Install
, base::Unretained(this)));
57 void WebstoreBindings::Install(
58 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
59 content::RenderView
* render_view
= context()->GetRenderView();
63 // The first two arguments indicate whether or not there are install stage
64 // or download progress listeners.
65 int listener_mask
= 0;
66 CHECK(args
[0]->IsBoolean());
67 if (args
[0]->BooleanValue())
68 listener_mask
|= api::webstore::INSTALL_STAGE_LISTENER
;
69 CHECK(args
[1]->IsBoolean());
70 if (args
[1]->BooleanValue())
71 listener_mask
|= api::webstore::DOWNLOAD_PROGRESS_LISTENER
;
73 std::string preferred_store_link_url
;
74 if (!args
[2]->IsUndefined()) {
75 CHECK(args
[2]->IsString());
76 preferred_store_link_url
= std::string(*v8::String::Utf8Value(args
[2]));
79 std::string webstore_item_id
;
81 WebFrame
* frame
= context()->web_frame();
83 if (!GetWebstoreItemIdFromFrame(
84 frame
, preferred_store_link_url
, &webstore_item_id
, &error
)) {
85 args
.GetIsolate()->ThrowException(
86 v8::String::NewFromUtf8(args
.GetIsolate(), error
.c_str()));
90 int install_id
= g_next_install_id
++;
92 Send(new ExtensionHostMsg_InlineWebstoreInstall(render_view
->GetRoutingID(),
96 frame
->document().url(),
99 args
.GetReturnValue().Set(static_cast<int32_t>(install_id
));
103 bool WebstoreBindings::GetWebstoreItemIdFromFrame(
104 WebFrame
* frame
, const std::string
& preferred_store_link_url
,
105 std::string
* webstore_item_id
, std::string
* error
) {
106 if (frame
!= frame
->top()) {
107 *error
= kNotInTopFrameError
;
111 if (!WebUserGestureIndicator::isProcessingUserGesture()) {
112 *error
= kNotUserGestureError
;
116 WebDocument document
= frame
->document();
117 if (document
.isNull()) {
118 *error
= kNoWebstoreItemLinkFoundError
;
122 WebElement head
= document
.head();
124 *error
= kNoWebstoreItemLinkFoundError
;
128 GURL webstore_base_url
=
129 GURL(extension_urls::GetWebstoreItemDetailURLPrefix());
130 WebNodeList children
= head
.childNodes();
131 for (unsigned i
= 0; i
< children
.length(); ++i
) {
132 WebNode child
= children
.item(i
);
133 if (!child
.isElementNode())
135 WebElement elem
= child
.to
<WebElement
>();
137 if (!elem
.hasTagName("link") || !elem
.hasAttribute("rel") ||
138 !elem
.hasAttribute("href"))
141 std::string rel
= elem
.getAttribute("rel").utf8();
142 if (!LowerCaseEqualsASCII(rel
, kWebstoreLinkRelation
))
145 std::string
webstore_url_string(elem
.getAttribute("href").utf8());
147 if (!preferred_store_link_url
.empty() &&
148 preferred_store_link_url
!= webstore_url_string
) {
152 GURL webstore_url
= GURL(webstore_url_string
);
153 if (!webstore_url
.is_valid()) {
154 *error
= kInvalidWebstoreItemUrlError
;
158 if (webstore_url
.scheme() != webstore_base_url
.scheme() ||
159 webstore_url
.host() != webstore_base_url
.host() ||
161 webstore_url
.path(), webstore_base_url
.path(), true)) {
162 *error
= kInvalidWebstoreItemUrlError
;
166 std::string candidate_webstore_item_id
= webstore_url
.path().substr(
167 webstore_base_url
.path().length());
168 if (!extensions::Extension::IdIsValid(candidate_webstore_item_id
)) {
169 *error
= kInvalidWebstoreItemUrlError
;
173 std::string reconstructed_webstore_item_url_string
=
174 extension_urls::GetWebstoreItemDetailURLPrefix() +
175 candidate_webstore_item_id
;
176 if (reconstructed_webstore_item_url_string
!= webstore_url_string
) {
177 *error
= kInvalidWebstoreItemUrlError
;
181 *webstore_item_id
= candidate_webstore_item_id
;
185 *error
= kNoWebstoreItemLinkFoundError
;
189 bool WebstoreBindings::OnMessageReceived(const IPC::Message
& message
) {
190 IPC_BEGIN_MESSAGE_MAP(WebstoreBindings
, message
)
191 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineWebstoreInstallResponse
,
192 OnInlineWebstoreInstallResponse
)
193 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineInstallStageChanged
,
194 OnInlineInstallStageChanged
)
195 IPC_MESSAGE_HANDLER(ExtensionMsg_InlineInstallDownloadProgress
,
196 OnInlineInstallDownloadProgress
)
197 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
198 IPC_END_MESSAGE_MAP()
202 void WebstoreBindings::OnInlineWebstoreInstallResponse(
205 const std::string
& error
) {
206 v8::Isolate
* isolate
= context()->isolate();
207 v8::HandleScope
handle_scope(isolate
);
208 v8::Context::Scope
context_scope(context()->v8_context());
209 v8::Handle
<v8::Value
> argv
[] = {
210 v8::Integer::New(isolate
, install_id
),
211 v8::Boolean::New(isolate
, success
),
212 v8::String::NewFromUtf8(isolate
, error
.c_str())
214 context()->module_system()->CallModuleMethod(
215 "webstore", "onInstallResponse", arraysize(argv
), argv
);
218 void WebstoreBindings::OnInlineInstallStageChanged(int stage
) {
219 const char* stage_string
= NULL
;
220 api::webstore::InstallStage install_stage
=
221 static_cast<api::webstore::InstallStage
>(stage
);
222 switch (install_stage
) {
223 case api::webstore::INSTALL_STAGE_DOWNLOADING
:
224 stage_string
= api::webstore::kInstallStageDownloading
;
226 case api::webstore::INSTALL_STAGE_INSTALLING
:
227 stage_string
= api::webstore::kInstallStageInstalling
;
230 v8::Isolate
* isolate
= context()->isolate();
231 v8::HandleScope
handle_scope(isolate
);
232 v8::Context::Scope
context_scope(context()->v8_context());
233 v8::Handle
<v8::Value
> argv
[] = {
234 v8::String::NewFromUtf8(isolate
, stage_string
)};
235 context()->module_system()->CallModuleMethod(
236 "webstore", "onInstallStageChanged", arraysize(argv
), argv
);
239 void WebstoreBindings::OnInlineInstallDownloadProgress(int percent_downloaded
) {
240 v8::Isolate
* isolate
= context()->isolate();
241 v8::HandleScope
handle_scope(isolate
);
242 v8::Context::Scope
context_scope(context()->v8_context());
243 v8::Handle
<v8::Value
> argv
[] = {
244 v8::Number::New(isolate
, percent_downloaded
/ 100.0)};
245 context()->module_system()->CallModuleMethod(
246 "webstore", "onDownloadProgress", arraysize(argv
), argv
);
249 } // namespace extensions