Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: chrome/renderer/extensions/chrome_webstore_bindings.cc

Issue 7795032: Add link URL and success/failure callback parameters to chrome.webstore.install() (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/renderer/extensions/chrome_webstore_bindings.h" 5 #include "chrome/renderer/extensions/chrome_webstore_bindings.h"
6 6
7 #include "base/string_util.h" 7 #include "base/string_util.h"
8 #include "chrome/common/extensions/extension.h" 8 #include "chrome/common/extensions/extension.h"
9 #include "chrome/renderer/extensions/extension_render_view_helper.h" 9 #include "chrome/renderer/extensions/extension_render_view_helper.h"
10 #include "content/renderer/render_view.h" 10 #include "content/renderer/render_view.h"
11 #include "googleurl/src/gurl.h" 11 #include "googleurl/src/gurl.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" 12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" 13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" 15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeList.h" 16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeList.h"
17 #include "v8/include/v8.h" 17 #include "v8/include/v8.h"
18 18
19 using WebKit::WebDocument; 19 using WebKit::WebDocument;
20 using WebKit::WebElement; 20 using WebKit::WebElement;
21 using WebKit::WebFrame; 21 using WebKit::WebFrame;
22 using WebKit::WebNode; 22 using WebKit::WebNode;
23 using WebKit::WebNodeList; 23 using WebKit::WebNodeList;
24 24
25 namespace {
26
27 const char kWebstoreV8ExtensionName[] = "v8/ChromeWebstore";
28
25 const char kWebstoreLinkRelation[] = "chrome-webstore-item"; 29 const char kWebstoreLinkRelation[] = "chrome-webstore-item";
26 30
31 const char kPreferredStoreLinkUrlNotAString[] =
32 "The Chrome Web Store item link URL parameter must be a string.";
33 const char kSuccessCallbackNotAFunctionError[] =
34 "The success callback parameter must be a function.";
35 const char kFailureCallbackNotAFunctionError[] =
36 "The failure callback parameter must be a function.";
27 const char kNotInTopFrameError[] = 37 const char kNotInTopFrameError[] =
28 "Chrome Web Store installations can only be started by the top frame."; 38 "Chrome Web Store installations can only be started by the top frame.";
29 const char kNotUserGestureError[] = 39 const char kNotUserGestureError[] =
30 "Chrome Web Store installations can only be initated by a user gesture."; 40 "Chrome Web Store installations can only be initated by a user gesture.";
31 const char kNoWebstoreItemLinkFoundError[] = 41 const char kNoWebstoreItemLinkFoundError[] =
32 "No Chrome Web Store item link found."; 42 "No Chrome Web Store item link found.";
33 const char kInvalidWebstoreItemUrlError[] = 43 const char kInvalidWebstoreItemUrlError[] =
34 "Invalid Chrome Web Store item URL."; 44 "Invalid Chrome Web Store item URL.";
35 45
36 namespace extensions_v8 { 46 // chrome.webstore.install() calls generate an install ID so that the install's
47 // callbacks may be fired when the browser notifies us of install completion
48 // (successful or not) via HandleInstallResponse.
49 int next_install_id;
37 50
38 static const char* const kWebstoreExtensionName = "v8/ChromeWebstore"; 51 // Callbacks are kept track of in maps keyed by install ID. Values are weak
52 // references to functions. Entries are automatically removed when functions
53 // get garbage collected.
54 typedef std::map<int, v8::Persistent<v8::Function> > CallbackMap;
39 55
40 class ChromeWebstoreExtensionWrapper : public v8::Extension { 56 CallbackMap success_callbacks;
Aaron Boodman 2011/09/06 23:24:32 Consider making a PendingWebstoreInstall class tha
Mihai Parparita -not on Chrome 2011/09/07 00:05:03 As discussed, this isn't that straightforward sinc
57 CallbackMap failure_callbacks;
Aaron Boodman 2011/09/06 23:24:32 Static non-pod variables are not allowed in Chromi
Mihai Parparita -not on Chrome 2011/09/07 00:05:03 Done.
58
59 // Extra data to be passed to MakeWeak/CallbackMapWeakReferenceCallback to know
60 // which entry to remove from which map.
61 struct CallbackMapData {
62 CallbackMap* callback_map;
63 int install_id;
64 };
65
66 // Disposes of a callback function and its corresponding entry in the callback
67 // map.
68 static void CallbackMapWeakReferenceCallback(
69 v8::Persistent<v8::Value> context, void* data) {
70 CallbackMapData* callback_map_data = static_cast<CallbackMapData*>(data);
71 callback_map_data->callback_map->erase(callback_map_data->install_id);
72
73 delete callback_map_data;
74 context.Dispose();
75 }
76
77 // Adds |callback_param| (assumed to be a function) to |callback_map| under the
78 // |install_id| key. Will be removed from the map when the value is about to be
79 // GCed.
80 static void AddToCallbackMap(int install_id,
81 v8::Local<v8::Value> callback_param,
82 CallbackMap* callback_map) {
83 CHECK(callback_param->IsFunction());
84 CallbackMapData* callback_map_data = new CallbackMapData();
85 callback_map_data->install_id = install_id;
86 callback_map_data->callback_map = callback_map;
87
88 v8::Local<v8::Function> function = v8::Function::Cast(*callback_param);
89 v8::Persistent<v8::Function> wrapper =
90 v8::Persistent<v8::Function>::New(function);
91 (*callback_map)[install_id] = wrapper;
92 wrapper.MakeWeak(callback_map_data, CallbackMapWeakReferenceCallback);
93 }
94
95 } // anonymous namespace
96
97 class ExtensionImpl : public v8::Extension {
41 public: 98 public:
42 ChromeWebstoreExtensionWrapper() : 99 ExtensionImpl() :
43 v8::Extension( 100 v8::Extension(
44 kWebstoreExtensionName, 101 kWebstoreV8ExtensionName,
45 "var chrome;" 102 "var chrome = chrome || {};"
46 "if (!chrome)"
47 " chrome = {};"
48 "if (!chrome.webstore) {" 103 "if (!chrome.webstore) {"
49 " chrome.webstore = new function() {" 104 " chrome.webstore = new function() {"
50 " native function Install();" 105 " native function Install(preferredStoreUrl, onSuccess, onFailure);"
51 " this.install = Install;" 106 " this.install = Install;"
52 " };" 107 " };"
53 "}") { 108 "}") {
54 } 109 }
55 110
56 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 111 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
57 v8::Handle<v8::String> name) { 112 v8::Handle<v8::String> name) {
58 if (name->Equals(v8::String::New("Install"))) { 113 if (name->Equals(v8::String::New("Install"))) {
59 return v8::FunctionTemplate::New(Install); 114 return v8::FunctionTemplate::New(Install);
60 } else { 115 } else {
61 return v8::Handle<v8::FunctionTemplate>(); 116 return v8::Handle<v8::FunctionTemplate>();
62 } 117 }
63 } 118 }
64 119
65 static v8::Handle<v8::Value> Install(const v8::Arguments& args) { 120 static v8::Handle<v8::Value> Install(const v8::Arguments& args) {
66 WebFrame* frame = WebFrame::frameForCurrentContext(); 121 WebFrame* frame = WebFrame::frameForCurrentContext();
67 if (!frame || !frame->view()) 122 if (!frame || !frame->view())
68 return v8::Undefined(); 123 return v8::Undefined();
69 124
70 RenderView* render_view = RenderView::FromWebView(frame->view()); 125 RenderView* render_view = RenderView::FromWebView(frame->view());
71 if (!render_view) 126 if (!render_view)
72 return v8::Undefined(); 127 return v8::Undefined();
73 128
129 std::string preferred_store_link_url;
130 if (args.Length() >= 1 && !args[0]->IsUndefined()) {
131 if (args[0]->IsString()) {
132 preferred_store_link_url = std::string(*v8::String::Utf8Value(args[0]));
133 } else {
134 v8::ThrowException(v8::String::New(kPreferredStoreLinkUrlNotAString));
135 return v8::Undefined();
136 }
137 }
138
74 std::string webstore_item_id; 139 std::string webstore_item_id;
75 std::string error; 140 std::string error;
76 if (GetWebstoreItemIdFromFrame(frame, &webstore_item_id, &error)) { 141 if (!GetWebstoreItemIdFromFrame(
77 ExtensionRenderViewHelper* helper = 142 frame, preferred_store_link_url, &webstore_item_id, &error)) {
78 ExtensionRenderViewHelper::Get(render_view);
79 helper->InlineWebstoreInstall(webstore_item_id);
80 } else {
81 v8::ThrowException(v8::String::New(error.c_str())); 143 v8::ThrowException(v8::String::New(error.c_str()));
144 return v8::Undefined();
82 } 145 }
83 146
147 int install_id = next_install_id++;
148 if (args.Length() >= 2 && !args[1]->IsUndefined()) {
149 if (args[1]->IsFunction()) {
150 AddToCallbackMap(install_id, args[1], &success_callbacks);
151 } else {
152 v8::ThrowException(v8::String::New(kSuccessCallbackNotAFunctionError));
153 return v8::Undefined();
154 }
155 }
156 if (args.Length() >= 3 && !args[2]->IsUndefined()) {
157 if (args[2]->IsFunction()) {
158 AddToCallbackMap(install_id, args[2], &failure_callbacks);
159 } else {
160 v8::ThrowException(v8::String::New(kFailureCallbackNotAFunctionError));
161 return v8::Undefined();
162 }
163 }
164
165 ExtensionRenderViewHelper* helper =
166 ExtensionRenderViewHelper::Get(render_view);
167 helper->InlineWebstoreInstall(
168 install_id, webstore_item_id, frame->document().url());
84 return v8::Undefined(); 169 return v8::Undefined();
85 } 170 }
86 171
87 private: 172 private:
88 // Extracts a Web Store item ID from a <link rel="chrome-webstore-item" 173 // Extracts a Web Store item ID from a <link rel="chrome-webstore-item"
89 // href="https://chrome.google.com/webstore/detail/id"> node found in the 174 // href="https://chrome.google.com/webstore/detail/id"> node found in the
90 // frame. On success, true will be returned and the |webstore_item_id| 175 // frame. On success, true will be returned and the |webstore_item_id|
91 // parameter will be populated with the ID. On failure, false will be returned 176 // parameter will be populated with the ID. On failure, false will be returned
92 // and |error| will be populated with the error. 177 // and |error| will be populated with the error.
93 static bool GetWebstoreItemIdFromFrame( 178 static bool GetWebstoreItemIdFromFrame(
94 WebFrame* frame, std::string* webstore_item_id, std::string* error) { 179 WebFrame* frame, const std::string& preferred_store_link_url,
180 std::string* webstore_item_id, std::string* error) {
95 if (frame != frame->top()) { 181 if (frame != frame->top()) {
96 *error = kNotInTopFrameError; 182 *error = kNotInTopFrameError;
97 return false; 183 return false;
98 } 184 }
99 185
100 if (!frame->isProcessingUserGesture()) { 186 if (!frame->isProcessingUserGesture()) {
101 *error = kNotUserGestureError; 187 *error = kNotUserGestureError;
102 return false; 188 return false;
103 } 189 }
104 190
(...skipping 20 matching lines...) Expand all
125 211
126 if (!elem.hasTagName("link") || !elem.hasAttribute("rel") || 212 if (!elem.hasTagName("link") || !elem.hasAttribute("rel") ||
127 !elem.hasAttribute("href")) 213 !elem.hasAttribute("href"))
128 continue; 214 continue;
129 215
130 std::string rel = elem.getAttribute("rel").utf8(); 216 std::string rel = elem.getAttribute("rel").utf8();
131 if (!LowerCaseEqualsASCII(rel, kWebstoreLinkRelation)) 217 if (!LowerCaseEqualsASCII(rel, kWebstoreLinkRelation))
132 continue; 218 continue;
133 219
134 std::string webstore_url_string(elem.getAttribute("href").utf8()); 220 std::string webstore_url_string(elem.getAttribute("href").utf8());
221
222 if (!preferred_store_link_url.empty() &&
223 preferred_store_link_url != webstore_url_string) {
224 continue;
225 }
226
135 GURL webstore_url = GURL(webstore_url_string); 227 GURL webstore_url = GURL(webstore_url_string);
136 if (!webstore_url.is_valid()) { 228 if (!webstore_url.is_valid()) {
137 *error = kInvalidWebstoreItemUrlError; 229 *error = kInvalidWebstoreItemUrlError;
138 return false; 230 return false;
139 } 231 }
140 232
141 if (webstore_url.scheme() != webstore_base_url.scheme() || 233 if (webstore_url.scheme() != webstore_base_url.scheme() ||
142 webstore_url.host() != webstore_base_url.host() || 234 webstore_url.host() != webstore_base_url.host() ||
143 !StartsWithASCII( 235 !StartsWithASCII(
144 webstore_url.path(), webstore_base_url.path(), true)) { 236 webstore_url.path(), webstore_base_url.path(), true)) {
(...skipping 18 matching lines...) Expand all
163 255
164 *webstore_item_id = candidate_webstore_item_id; 256 *webstore_item_id = candidate_webstore_item_id;
165 return true; 257 return true;
166 } 258 }
167 259
168 *error = kNoWebstoreItemLinkFoundError; 260 *error = kNoWebstoreItemLinkFoundError;
169 return false; 261 return false;
170 } 262 }
171 }; 263 };
172 264
265 // static
173 v8::Extension* ChromeWebstoreExtension::Get() { 266 v8::Extension* ChromeWebstoreExtension::Get() {
174 return new ChromeWebstoreExtensionWrapper(); 267 return new ExtensionImpl();
175 } 268 }
176 269
177 } // namespace extensions_v8 270 // static
271 void ChromeWebstoreExtension::HandleInstallResponse(int install_id,
272 bool success,
273 const std::string& error) {
274 const CallbackMap& callback_map =
275 success ? success_callbacks : failure_callbacks;
276 CallbackMap::const_iterator iter = callback_map.find(install_id);
277
278 if (iter == callback_map.end() || iter->second.IsEmpty()) return;
279 const v8::Persistent<v8::Function>& function = (*iter).second;
280
281 v8::HandleScope handle_scope;
282 v8::Context::Scope context_scope(function->CreationContext());
283 v8::Handle<v8::Value> argv[1];
284 argv[0] = v8::String::New(error.c_str());
285 function->Call(v8::Object::New(), arraysize(argv), argv);
Aaron Boodman 2011/09/06 23:24:32 You can remove the function from the map after thi
Mihai Parparita -not on Chrome 2011/09/07 00:05:03 Done.
286 }
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/chrome_webstore_bindings.h ('k') | chrome/renderer/extensions/extension_render_view_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698