Commit a221ef09 authored by mihaip@chromium.org's avatar mihaip@chromium.org

Add link URL and success/failure callback parameters to chrome.webstore.install()

The link URL is used to select between multiple <link rel="chrome-webstore-item">
elements on the page.

To keep track of callbacks, an install ID is generated at the start of each
chrome.webstore.install() call, so that the browser knows which ones to invoke
when an install completes.

Also adds validation of install requesting URL against the item's verified
domain (wasn't done until now since it wasn't testable).

R=aa@chromium.org
BUG=93380

Review URL: http://codereview.chromium.org/7795032

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99889 0039d316-1c4b-4281-b951-d872f2087c98
parent cd7fa99f
...@@ -149,14 +149,16 @@ void ExtensionTabHelper::OnInstallApplication(const WebApplicationInfo& info) { ...@@ -149,14 +149,16 @@ void ExtensionTabHelper::OnInstallApplication(const WebApplicationInfo& info) {
} }
void ExtensionTabHelper::OnInlineWebstoreInstall( void ExtensionTabHelper::OnInlineWebstoreInstall(
const std::string& webstore_item_id) { int install_id,
const std::string& webstore_item_id,
const GURL& requestor_url) {
if (!CommandLine::ForCurrentProcess()->HasSwitch( if (!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableInlineWebstoreInstall)) { switches::kEnableInlineWebstoreInstall)) {
return; return;
} }
scoped_refptr<WebstoreInlineInstaller> installer(new WebstoreInlineInstaller( scoped_refptr<WebstoreInlineInstaller> installer(new WebstoreInlineInstaller(
tab_contents(), webstore_item_id, this)); tab_contents(), install_id, webstore_item_id, requestor_url, this));
installer->BeginInstall(); installer->BeginInstall();
} }
...@@ -208,13 +210,15 @@ Browser* ExtensionTabHelper::GetBrowser() { ...@@ -208,13 +210,15 @@ Browser* ExtensionTabHelper::GetBrowser() {
return NULL; return NULL;
} }
void ExtensionTabHelper::OnInlineInstallSuccess() { void ExtensionTabHelper::OnInlineInstallSuccess(int install_id) {
Send(new ExtensionMsg_InlineWebstoreInstallResponse(routing_id(), true, "")); Send(new ExtensionMsg_InlineWebstoreInstallResponse(
routing_id(), install_id, true, ""));
} }
void ExtensionTabHelper::OnInlineInstallFailure(const std::string& error) { void ExtensionTabHelper::OnInlineInstallFailure(int install_id,
const std::string& error) {
Send(new ExtensionMsg_InlineWebstoreInstallResponse( Send(new ExtensionMsg_InlineWebstoreInstallResponse(
routing_id(), false, error)); routing_id(), install_id, false, error));
} }
TabContents* ExtensionTabHelper::GetAssociatedTabContents() const { TabContents* ExtensionTabHelper::GetAssociatedTabContents() const {
......
...@@ -97,7 +97,9 @@ class ExtensionTabHelper : public TabContentsObserver, ...@@ -97,7 +97,9 @@ class ExtensionTabHelper : public TabContentsObserver,
// Message handlers. // Message handlers.
void OnDidGetApplicationInfo(int32 page_id, const WebApplicationInfo& info); void OnDidGetApplicationInfo(int32 page_id, const WebApplicationInfo& info);
void OnInstallApplication(const WebApplicationInfo& info); void OnInstallApplication(const WebApplicationInfo& info);
void OnInlineWebstoreInstall(const std::string& webstore_item_id); void OnInlineWebstoreInstall(int install_id,
const std::string& webstore_item_id,
const GURL& requestor_url);
void OnRequest(const ExtensionHostMsg_Request_Params& params); void OnRequest(const ExtensionHostMsg_Request_Params& params);
// App extensions related methods: // App extensions related methods:
...@@ -111,8 +113,9 @@ class ExtensionTabHelper : public TabContentsObserver, ...@@ -111,8 +113,9 @@ class ExtensionTabHelper : public TabContentsObserver,
int index) OVERRIDE; int index) OVERRIDE;
// WebstoreInlineInstaller::Delegate. // WebstoreInlineInstaller::Delegate.
virtual void OnInlineInstallSuccess() OVERRIDE; virtual void OnInlineInstallSuccess(int install_id) OVERRIDE;
virtual void OnInlineInstallFailure(const std::string& error) OVERRIDE; virtual void OnInlineInstallFailure(int install_id,
const std::string& error) OVERRIDE;
// Data for app extensions --------------------------------------------------- // Data for app extensions ---------------------------------------------------
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
const char kWebstoreDomain[] = "cws.com"; const char kWebstoreDomain[] = "cws.com";
const char kAppDomain[] = "app.com"; const char kAppDomain[] = "app.com";
const char kNonAppDomain[] = "nonapp.com";
class WebstoreInlineInstallTest : public InProcessBrowserTest { class WebstoreInlineInstallTest : public InProcessBrowserTest {
public: public:
...@@ -52,6 +53,7 @@ class WebstoreInlineInstallTest : public InProcessBrowserTest { ...@@ -52,6 +53,7 @@ class WebstoreInlineInstallTest : public InProcessBrowserTest {
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
host_resolver()->AddRule(kWebstoreDomain, "127.0.0.1"); host_resolver()->AddRule(kWebstoreDomain, "127.0.0.1");
host_resolver()->AddRule(kAppDomain, "127.0.0.1"); host_resolver()->AddRule(kAppDomain, "127.0.0.1");
host_resolver()->AddRule(kNonAppDomain, "127.0.0.1");
} }
protected: protected:
...@@ -65,6 +67,16 @@ class WebstoreInlineInstallTest : public InProcessBrowserTest { ...@@ -65,6 +67,16 @@ class WebstoreInlineInstallTest : public InProcessBrowserTest {
return page_url.ReplaceComponents(replace_host); return page_url.ReplaceComponents(replace_host);
} }
void RunInlineInstallTest() {
bool result = false;
std::string script =
StringPrintf("runTest('%s')", test_gallery_url_.c_str());
ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
browser()->GetSelectedTabContents()->render_view_host(), L"",
UTF8ToWide(script), &result));
EXPECT_TRUE(result);
}
std::string test_gallery_url_; std::string test_gallery_url_;
}; };
...@@ -78,12 +90,7 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) { ...@@ -78,12 +90,7 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) {
ui_test_utils::NavigateToURL( ui_test_utils::NavigateToURL(
browser(), GenerateTestServerUrl(kAppDomain, "install.html")); browser(), GenerateTestServerUrl(kAppDomain, "install.html"));
bool result = false; RunInlineInstallTest();
std::string script = StringPrintf("runTest('%s')", test_gallery_url_.c_str());
ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
browser()->GetSelectedTabContents()->render_view_host(), L"",
UTF8ToWide(script), &result));
EXPECT_TRUE(result);
load_signal.Wait(); load_signal.Wait();
...@@ -92,6 +99,16 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) { ...@@ -92,6 +99,16 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) {
EXPECT_TRUE(extension != NULL); EXPECT_TRUE(extension != NULL);
} }
IN_PROC_BROWSER_TEST_F(
WebstoreInlineInstallTest, InstallNotAllowedFromNonVerifiedDomains) {
SetExtensionInstallDialogForManifestAutoConfirmForTests(false);
ui_test_utils::NavigateToURL(
browser(),
GenerateTestServerUrl(kNonAppDomain, "install-non-verified-domain.html"));
RunInlineInstallTest();
}
// Flakily fails on Linux. http://crbug.com/95280 // Flakily fails on Linux. http://crbug.com/95280
#if defined(OS_LINUX) #if defined(OS_LINUX)
#define MAYBE_FindLink FLAKY_FindLink #define MAYBE_FindLink FLAKY_FindLink
...@@ -103,10 +120,13 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, MAYBE_FindLink) { ...@@ -103,10 +120,13 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, MAYBE_FindLink) {
ui_test_utils::NavigateToURL( ui_test_utils::NavigateToURL(
browser(), GenerateTestServerUrl(kAppDomain, "find_link.html")); browser(), GenerateTestServerUrl(kAppDomain, "find_link.html"));
bool result = false; RunInlineInstallTest();
std::string script = StringPrintf("runTest('%s')", test_gallery_url_.c_str()); }
ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
browser()->GetSelectedTabContents()->render_view_host(), L"", IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, ArgumentValidation) {
UTF8ToWide(script), &result)); SetExtensionInstallDialogForManifestAutoConfirmForTests(false);
EXPECT_TRUE(result); ui_test_utils::NavigateToURL(
browser(), GenerateTestServerUrl(kAppDomain, "argument_validation.html"));
RunInlineInstallTest();
} }
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
#include "chrome/common/chrome_utility_messages.h" #include "chrome/common/chrome_utility_messages.h"
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/url_pattern.h"
#include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/utility_process_host.h" #include "content/browser/utility_process_host.h"
#include "googleurl/src/gurl.h"
#include "net/base/escape.h" #include "net/base/escape.h"
#include "net/url_request/url_request_status.h" #include "net/url_request/url_request_status.h"
...@@ -28,12 +28,17 @@ const char kLocalizedDescriptionKey[] = "localized_description"; ...@@ -28,12 +28,17 @@ const char kLocalizedDescriptionKey[] = "localized_description";
const char kUsersKey[] = "users"; const char kUsersKey[] = "users";
const char kAverageRatingKey[] = "average_rating"; const char kAverageRatingKey[] = "average_rating";
const char kRatingCountKey[] = "rating_count"; const char kRatingCountKey[] = "rating_count";
const char kVerifiedSiteKey[] = "verified_site";
const char kInvalidWebstoreItemId[] = "Invalid webstore item ID"; const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
const char kWebstoreRequestError[] = "Could not fetch data from webstore"; const char kWebstoreRequestError[] =
const char kInvalidWebstoreResponseError[] = "Invalid webstore reponse"; "Could not fetch data from the Chrome Web Store";
const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
const char kInvalidManifestError[] = "Invalid manifest"; const char kInvalidManifestError[] = "Invalid manifest";
const char kUserCancelledError[] = "User cancelled install"; const char kUserCancelledError[] = "User cancelled install";
const char kNotFromVerifiedSite[] =
"Installs can only be initiated by the Chrome Web Store item's verified "
"site";
class SafeWebstoreResponseParser : public UtilityProcessHost::Client { class SafeWebstoreResponseParser : public UtilityProcessHost::Client {
public: public:
...@@ -127,10 +132,14 @@ class SafeWebstoreResponseParser : public UtilityProcessHost::Client { ...@@ -127,10 +132,14 @@ class SafeWebstoreResponseParser : public UtilityProcessHost::Client {
}; };
WebstoreInlineInstaller::WebstoreInlineInstaller(TabContents* tab_contents, WebstoreInlineInstaller::WebstoreInlineInstaller(TabContents* tab_contents,
int install_id,
std::string webstore_item_id, std::string webstore_item_id,
GURL requestor_url,
Delegate* delegate) Delegate* delegate)
: tab_contents_(tab_contents), : tab_contents_(tab_contents),
install_id_(install_id),
id_(webstore_item_id), id_(webstore_item_id),
requestor_url_(requestor_url),
delegate_(delegate) {} delegate_(delegate) {}
WebstoreInlineInstaller::~WebstoreInlineInstaller() { WebstoreInlineInstaller::~WebstoreInlineInstaller() {
...@@ -225,6 +234,26 @@ void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess( ...@@ -225,6 +234,26 @@ void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess(
} }
} }
// Verified site is optional
if (webstore_data->HasKey(kVerifiedSiteKey)) {
std::string verified_site_domain;
if (!webstore_data->GetString(kVerifiedSiteKey, &verified_site_domain)) {
CompleteInstall(kInvalidWebstoreResponseError);
return;
}
URLPattern verified_site_pattern(URLPattern::SCHEME_ALL);
verified_site_pattern.SetScheme("*");
verified_site_pattern.SetHost(verified_site_domain);
verified_site_pattern.SetMatchSubdomains(true);
verified_site_pattern.SetPath("/*");
if (!verified_site_pattern.MatchesURL(requestor_url_)) {
CompleteInstall(kNotFromVerifiedSite);
return;
}
}
scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
this, this,
manifest, manifest,
...@@ -312,9 +341,9 @@ void WebstoreInlineInstaller::InstallUIAbort(bool user_initiated) { ...@@ -312,9 +341,9 @@ void WebstoreInlineInstaller::InstallUIAbort(bool user_initiated) {
void WebstoreInlineInstaller::CompleteInstall(const std::string& error) { void WebstoreInlineInstaller::CompleteInstall(const std::string& error) {
if (error.empty()) { if (error.empty()) {
delegate_->OnInlineInstallSuccess(); delegate_->OnInlineInstallSuccess(install_id_);
} else { } else {
delegate_->OnInlineInstallFailure(error); delegate_->OnInlineInstallFailure(install_id_, error);
} }
Release(); // Matches the AddRef in BeginInstall. Release(); // Matches the AddRef in BeginInstall.
} }
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/webstore_install_helper.h" #include "chrome/browser/extensions/webstore_install_helper.h"
#include "content/common/url_fetcher.h" #include "content/common/url_fetcher.h"
#include "googleurl/src/gurl.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
class TabContents; class TabContents;
...@@ -32,12 +33,15 @@ class WebstoreInlineInstaller ...@@ -32,12 +33,15 @@ class WebstoreInlineInstaller
public: public:
class Delegate { class Delegate {
public: public:
virtual void OnInlineInstallSuccess() = 0; virtual void OnInlineInstallSuccess(int install_id) = 0;
virtual void OnInlineInstallFailure(const std::string& error) = 0; virtual void OnInlineInstallFailure(int install_id,
const std::string& error) = 0;
}; };
WebstoreInlineInstaller(TabContents* tab_contents, WebstoreInlineInstaller(TabContents* tab_contents,
int install_id,
std::string webstore_item_id, std::string webstore_item_id,
GURL requestor_url,
Delegate* d); Delegate* d);
void BeginInstall(); void BeginInstall();
...@@ -82,7 +86,9 @@ class WebstoreInlineInstaller ...@@ -82,7 +86,9 @@ class WebstoreInlineInstaller
void CompleteInstall(const std::string& error); void CompleteInstall(const std::string& error);
TabContents* tab_contents_; TabContents* tab_contents_;
int install_id_;
std::string id_; std::string id_;
GURL requestor_url_;
Delegate* delegate_; Delegate* delegate_;
// For fetching webstore JSON data. // For fetching webstore JSON data.
......
...@@ -315,10 +315,13 @@ IPC_MESSAGE_ROUTED1(ExtensionHostMsg_InstallApplication, ...@@ -315,10 +315,13 @@ IPC_MESSAGE_ROUTED1(ExtensionHostMsg_InstallApplication,
WebApplicationInfo) WebApplicationInfo)
// Sent by the renderer to implement chrome.webstore.install(). // Sent by the renderer to implement chrome.webstore.install().
IPC_MESSAGE_ROUTED1(ExtensionHostMsg_InlineWebstoreInstall, IPC_MESSAGE_ROUTED3(ExtensionHostMsg_InlineWebstoreInstall,
std::string /* Web Store item ID */) int32 /* install id */,
std::string /* Web Store item ID */,
GURL /* requestor URL */)
// Send to renderer once the installation mentioned above is complete. // Send to renderer once the installation mentioned above is complete.
IPC_MESSAGE_ROUTED2(ExtensionMsg_InlineWebstoreInstallResponse, IPC_MESSAGE_ROUTED3(ExtensionMsg_InlineWebstoreInstallResponse,
int32 /* install id */,
bool /* whether the install was successful */, bool /* whether the install was successful */,
std::string /* error */) std::string /* error */)
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/renderer/extensions/chrome_webstore_bindings.h" #include "chrome/renderer/extensions/chrome_webstore_bindings.h"
#include "base/lazy_instance.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
#include "chrome/renderer/extensions/extension_render_view_helper.h" #include "chrome/renderer/extensions/extension_render_view_helper.h"
...@@ -22,8 +23,18 @@ using WebKit::WebFrame; ...@@ -22,8 +23,18 @@ using WebKit::WebFrame;
using WebKit::WebNode; using WebKit::WebNode;
using WebKit::WebNodeList; using WebKit::WebNodeList;
namespace {
const char kWebstoreV8ExtensionName[] = "v8/ChromeWebstore";
const char kWebstoreLinkRelation[] = "chrome-webstore-item"; const char kWebstoreLinkRelation[] = "chrome-webstore-item";
const char kPreferredStoreLinkUrlNotAString[] =
"The Chrome Web Store item link URL parameter must be a string.";
const char kSuccessCallbackNotAFunctionError[] =
"The success callback parameter must be a function.";
const char kFailureCallbackNotAFunctionError[] =
"The failure callback parameter must be a function.";
const char kNotInTopFrameError[] = const char kNotInTopFrameError[] =
"Chrome Web Store installations can only be started by the top frame."; "Chrome Web Store installations can only be started by the top frame.";
const char kNotUserGestureError[] = const char kNotUserGestureError[] =
...@@ -33,21 +44,65 @@ const char kNoWebstoreItemLinkFoundError[] = ...@@ -33,21 +44,65 @@ const char kNoWebstoreItemLinkFoundError[] =
const char kInvalidWebstoreItemUrlError[] = const char kInvalidWebstoreItemUrlError[] =
"Invalid Chrome Web Store item URL."; "Invalid Chrome Web Store item URL.";
namespace extensions_v8 { // chrome.webstore.install() calls generate an install ID so that the install's
// callbacks may be fired when the browser notifies us of install completion
// (successful or not) via HandleInstallResponse.
int g_next_install_id = 0;
// Callbacks are kept track of in maps keyed by install ID. Values are weak
// references to functions. Entries are automatically removed when functions
// get garbage collected.
typedef std::map<int, v8::Persistent<v8::Function> > CallbackMap;
base::LazyInstance<CallbackMap> g_success_callbacks(base::LINKER_INITIALIZED);
base::LazyInstance<CallbackMap> g_failure_callbacks(base::LINKER_INITIALIZED);
// Extra data to be passed to MakeWeak/RemoveFromCallbackMap to know which entry
// to remove from which map.
struct CallbackMapData {
CallbackMap* callback_map;
int install_id;
};
// Disposes of a callback function and its corresponding entry in the callback
// map.
static void RemoveFromCallbackMap(v8::Persistent<v8::Value> context,
void* data) {
CallbackMapData* callback_map_data = static_cast<CallbackMapData*>(data);
callback_map_data->callback_map->erase(callback_map_data->install_id);
delete callback_map_data;
context.Dispose();
}
static const char* const kWebstoreExtensionName = "v8/ChromeWebstore"; // Adds |callback_param| (assumed to be a function) to |callback_map| under the
// |install_id| key. Will be removed from the map when the value is about to be
// GCed.
static void AddToCallbackMap(int install_id,
v8::Local<v8::Value> callback_param,
CallbackMap* callback_map) {
CHECK(callback_param->IsFunction());
CallbackMapData* callback_map_data = new CallbackMapData();
callback_map_data->install_id = install_id;
callback_map_data->callback_map = callback_map;
class ChromeWebstoreExtensionWrapper : public v8::Extension { v8::Local<v8::Function> function = v8::Function::Cast(*callback_param);
v8::Persistent<v8::Function> wrapper =
v8::Persistent<v8::Function>::New(function);
(*callback_map)[install_id] = wrapper;
wrapper.MakeWeak(callback_map_data, RemoveFromCallbackMap);
}
} // anonymous namespace
class ExtensionImpl : public v8::Extension {
public: public:
ChromeWebstoreExtensionWrapper() : ExtensionImpl() :
v8::Extension( v8::Extension(
kWebstoreExtensionName, kWebstoreV8ExtensionName,
"var chrome;" "var chrome = chrome || {};"
"if (!chrome)"
" chrome = {};"
"if (!chrome.webstore) {" "if (!chrome.webstore) {"
" chrome.webstore = new function() {" " chrome.webstore = new function() {"
" native function Install();" " native function Install(preferredStoreUrl, onSuccess, onFailure);"
" this.install = Install;" " this.install = Install;"
" };" " };"
"}") { "}") {
...@@ -71,16 +126,46 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension { ...@@ -71,16 +126,46 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension {
if (!render_view) if (!render_view)
return v8::Undefined(); return v8::Undefined();
std::string preferred_store_link_url;
if (args.Length() >= 1 && !args[0]->IsUndefined()) {
if (args[0]->IsString()) {
preferred_store_link_url = std::string(*v8::String::Utf8Value(args[0]));
} else {
v8::ThrowException(v8::String::New(kPreferredStoreLinkUrlNotAString));
return v8::Undefined();
}
}
std::string webstore_item_id; std::string webstore_item_id;
std::string error; std::string error;
if (GetWebstoreItemIdFromFrame(frame, &webstore_item_id, &error)) { if (!GetWebstoreItemIdFromFrame(
ExtensionRenderViewHelper* helper = frame, preferred_store_link_url, &webstore_item_id, &error)) {
ExtensionRenderViewHelper::Get(render_view);
helper->InlineWebstoreInstall(webstore_item_id);
} else {
v8::ThrowException(v8::String::New(error.c_str())); v8::ThrowException(v8::String::New(error.c_str()));
return v8::Undefined();
} }
int install_id = g_next_install_id++;
if (args.Length() >= 2 && !args[1]->IsUndefined()) {
if (args[1]->IsFunction()) {
AddToCallbackMap(install_id, args[1], g_success_callbacks.Pointer());
} else {
v8::ThrowException(v8::String::New(kSuccessCallbackNotAFunctionError));
return v8::Undefined();
}
}
if (args.Length() >= 3 && !args[2]->IsUndefined()) {
if (args[2]->IsFunction()) {
AddToCallbackMap(install_id, args[2], g_failure_callbacks.Pointer());
} else {
v8::ThrowException(v8::String::New(kFailureCallbackNotAFunctionError));
return v8::Undefined();
}
}
ExtensionRenderViewHelper* helper =
ExtensionRenderViewHelper::Get(render_view);
helper->InlineWebstoreInstall(
install_id, webstore_item_id, frame->document().url());
return v8::Undefined(); return v8::Undefined();
} }
...@@ -91,7 +176,8 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension { ...@@ -91,7 +176,8 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension {
// parameter will be populated with the ID. On failure, false will be returned // parameter will be populated with the ID. On failure, false will be returned
// and |error| will be populated with the error. // and |error| will be populated with the error.
static bool GetWebstoreItemIdFromFrame( static bool GetWebstoreItemIdFromFrame(
WebFrame* frame, std::string* webstore_item_id, std::string* error) { WebFrame* frame, const std::string& preferred_store_link_url,
std::string* webstore_item_id, std::string* error) {
if (frame != frame->top()) { if (frame != frame->top()) {
*error = kNotInTopFrameError; *error = kNotInTopFrameError;
return false; return false;
...@@ -132,6 +218,12 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension { ...@@ -132,6 +218,12 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension {
continue; continue;
std::string webstore_url_string(elem.getAttribute("href").utf8()); std::string webstore_url_string(elem.getAttribute("href").utf8());
if (!preferred_store_link_url.empty() &&
preferred_store_link_url != webstore_url_string) {
continue;
}
GURL webstore_url = GURL(webstore_url_string); GURL webstore_url = GURL(webstore_url_string);
if (!webstore_url.is_valid()) { if (!webstore_url.is_valid()) {
*error = kInvalidWebstoreItemUrlError; *error = kInvalidWebstoreItemUrlError;
...@@ -170,8 +262,27 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension { ...@@ -170,8 +262,27 @@ class ChromeWebstoreExtensionWrapper : public v8::Extension {
} }
}; };
// static
v8::Extension* ChromeWebstoreExtension::Get() { v8::Extension* ChromeWebstoreExtension::Get() {
return new ChromeWebstoreExtensionWrapper(); return new ExtensionImpl();
} }
} // namespace extensions_v8 // static
void ChromeWebstoreExtension::HandleInstallResponse(int install_id,
bool success,
const std::string& error) {
CallbackMap* callback_map =
success ? g_success_callbacks.Pointer() : g_failure_callbacks.Pointer();
CallbackMap::iterator iter = callback_map->find(install_id);
if (iter == callback_map->end() || iter->second.IsEmpty()) return;
const v8::Persistent<v8::Function>& function = (*iter).second;
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(function->CreationContext());
v8::Handle<v8::Value> argv[1];
argv[0] = v8::String::New(error.c_str());
function->Call(v8::Object::New(), arraysize(argv), argv);
callback_map->erase(iter);
}
...@@ -10,17 +10,19 @@ ...@@ -10,17 +10,19 @@
#define CHROME_RENDERER_EXTENSIONS_CHROME_WEBSTORE_BINDINGS_H_ #define CHROME_RENDERER_EXTENSIONS_CHROME_WEBSTORE_BINDINGS_H_
#pragma once #pragma once
#include <string>
namespace v8 { namespace v8 {
class Extension; class Extension;
} }
namespace extensions_v8 {
class ChromeWebstoreExtension { class ChromeWebstoreExtension {
public: public:
static v8::Extension* Get(); static v8::Extension* Get();
};
} // namespace extensions_v8 static void HandleInstallResponse(int install_id,
bool success,
const std::string& error);
};
#endif // CHROME_RENDERER_EXTENSIONS_CHROME_WEBSTORE_BINDINGS_H_ #endif // CHROME_RENDERER_EXTENSIONS_CHROME_WEBSTORE_BINDINGS_H_
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/render_messages.h" #include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
#include "chrome/renderer/extensions/chrome_webstore_bindings.h"
#include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/extension_bindings_context.h" #include "chrome/renderer/extensions/extension_bindings_context.h"
#include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/extension_process_bindings.h"
...@@ -107,21 +108,16 @@ bool ExtensionRenderViewHelper::InstallWebApplicationUsingDefinitionFile( ...@@ -107,21 +108,16 @@ bool ExtensionRenderViewHelper::InstallWebApplicationUsingDefinitionFile(
} }
void ExtensionRenderViewHelper::InlineWebstoreInstall( void ExtensionRenderViewHelper::InlineWebstoreInstall(
std::string webstore_item_id) { int install_id, std::string webstore_item_id, GURL requestor_url) {
Send(new ExtensionHostMsg_InlineWebstoreInstall( Send(new ExtensionHostMsg_InlineWebstoreInstall(
routing_id(), webstore_item_id)); routing_id(), install_id, webstore_item_id, requestor_url));
} }
void ExtensionRenderViewHelper::OnInlineWebstoreInstallResponse( void ExtensionRenderViewHelper::OnInlineWebstoreInstallResponse(
int install_id,
bool success, bool success,
const std::string& error) { const std::string& error) {
// TODO(mihaip): dispatch these as events to the the WebFrame that initiated ChromeWebstoreExtension::HandleInstallResponse(install_id, success, error);
// the inline install.
if (success) {
VLOG(1) << "Inline install succeeded.";
} else {
VLOG(1) << "Inline install failed: " << error;
}
} }
bool ExtensionRenderViewHelper::OnMessageReceived(const IPC::Message& message) { bool ExtensionRenderViewHelper::OnMessageReceived(const IPC::Message& message) {
......
...@@ -42,7 +42,9 @@ class ExtensionRenderViewHelper ...@@ -42,7 +42,9 @@ class ExtensionRenderViewHelper
bool InstallWebApplicationUsingDefinitionFile(WebKit::WebFrame* frame, bool InstallWebApplicationUsingDefinitionFile(WebKit::WebFrame* frame,
string16* error); string16* error);
void InlineWebstoreInstall(std::string webstore_item_id); void InlineWebstoreInstall(int install_id,
std::string webstore_item_id,
GURL requestor_url);
int browser_window_id() const { return browser_window_id_; } int browser_window_id() const { return browser_window_id_; }
ViewType::Type view_type() const { return view_type_; } ViewType::Type view_type() const { return view_type_; }
...@@ -69,7 +71,8 @@ class ExtensionRenderViewHelper ...@@ -69,7 +71,8 @@ class ExtensionRenderViewHelper
void OnGetApplicationInfo(int page_id); void OnGetApplicationInfo(int page_id);
void OnNotifyRendererViewType(ViewType::Type view_type); void OnNotifyRendererViewType(ViewType::Type view_type);
void OnUpdateBrowserWindowId(int window_id); void OnUpdateBrowserWindowId(int window_id);
void OnInlineWebstoreInstallResponse(bool success, const std::string& error); void OnInlineWebstoreInstallResponse(
int install_id, bool success, const std::string& error);
// Callback triggered when we finish downloading the application definition // Callback triggered when we finish downloading the application definition
// file. // file.
......
...@@ -89,7 +89,7 @@ void ExtensionRendererContext::WebKitInitialized() { ...@@ -89,7 +89,7 @@ void ExtensionRendererContext::WebKitInitialized() {
if (CommandLine::ForCurrentProcess()->HasSwitch( if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableInlineWebstoreInstall)) { switches::kEnableInlineWebstoreInstall)) {
RegisterExtension(extensions_v8::ChromeWebstoreExtension::Get(), false); RegisterExtension(ChromeWebstoreExtension::Get(), false);
} }
// Add v8 extensions related to chrome extensions. // Add v8 extensions related to chrome extensions.
......
<!DOCTYPE html>
<html>
<head>
<link rel="chrome-webstore-item">
</head>
<body>
<script>
function runTest(galleryUrl) {
var storeUrl = galleryUrl + '/detail/ecglahbcnmdpdciemllbhojghbkagdje';
// Link URL has to be generated dynamically in order to include the right
// port number. The ID corresponds to the data in the "extension" directory.
document.getElementsByTagName('link')[0].href = storeUrl;
function runTests(tests) {
var failed = false;
tests.forEach(function(test) {
if (test.shouldPass) {
try {
test.func();
} catch (e) {
alert('unexpected exception: ' + e);
failed = true;
}
} else {
try {
test.func();
alert('unexpected pass: ' + func);
failed = true;
} catch (e) {
// Expected exception
}
}
});
window.domAutomationController.send(!failed);
}
function shouldPass(func) {
return {
func: func,
shouldPass: true
}
}
function shouldFail(func) {
return {
func: func,
shouldPass: false
}
}
var cwi = chrome.webstore.install;
runTests([
shouldPass(function() {cwi()}),
shouldPass(function() {cwi(undefined)}),
shouldPass(function() {cwi(undefined, undefined)}),
shouldPass(function() {cwi(undefined, undefined, undefined)}),
shouldPass(function() {cwi(storeUrl)}),
shouldPass(function() {cwi(undefined, function() {})}),
shouldPass(function() {cwi(undefined, undefined, function() {})}),
shouldFail(function() {cwi(123)}),
shouldFail(function() {cwi(undefined, 123)}),
shouldFail(function() {cwi(undefined, undefined, 123)})
]);
}
</script>
</body>
</html>
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
var NO_LINK_EXCEPTION = 'No Chrome Web Store item link found.'; var NO_LINK_EXCEPTION = 'No Chrome Web Store item link found.';
var INVALID_URL_EXCEPTION = 'Invalid Chrome Web Store item URL.'; var INVALID_URL_EXCEPTION = 'Invalid Chrome Web Store item URL.';
function checkNoLinkFound(expectedException) { function checkNoLinkFound(expectedException, opt_preferredStoreUrl) {
try { try {
chrome.webstore.install(); chrome.webstore.install(opt_preferredStoreUrl);
console.log('Exception should have been thrown'); console.log('Exception should have been thrown');
window.domAutomationController.send(false); window.domAutomationController.send(false);
return; return;
...@@ -55,6 +55,13 @@ ...@@ -55,6 +55,13 @@
galleryUrl + '/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm?foo=bar'; galleryUrl + '/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm?foo=bar';
checkNoLinkFound(INVALID_URL_EXCEPTION); checkNoLinkFound(INVALID_URL_EXCEPTION);
// Non-existent preferred link.
linkNode.rel = 'chrome-webstore-item';
linkNode.href = galleryUrl + '/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm';
checkNoLinkFound(
NO_LINK_EXCEPTION,
galleryUrl + '/detail/iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii');
// Successful installation is tested elsewhere // Successful installation is tested elsewhere
window.domAutomationController.send(true); window.domAutomationController.send(true);
} }
......
...@@ -3,5 +3,6 @@ ...@@ -3,5 +3,6 @@
"users": "371,674", "users": "371,674",
"average_rating": 4.36, "average_rating": 4.36,
"rating_count": 788, "rating_count": 788,
"verified_site": "app.com",
"manifest": "{\"name\":\"Inline Install Test Extension\",\"version\":\"0.1\",\"icons\":{\"128\":\"icon.png\"},\"permissions\":[\"tabs\"]}" "manifest": "{\"name\":\"Inline Install Test Extension\",\"version\":\"0.1\",\"icons\":{\"128\":\"icon.png\"},\"permissions\":[\"tabs\"]}"
} }
<!DOCTYPE html>
<html>
<head>
<link rel="chrome-webstore-item">
</head>
<body>
<script>
function runTest(galleryUrl) {
// Link URL has to be generated dynamically in order to include the right
// port number. The ID corresponds to the data in the "extension" directory.
document.getElementsByTagName('link')[0].href =
galleryUrl + '/detail/ecglahbcnmdpdciemllbhojghbkagdje';
try {
chrome.webstore.install(
undefined,
function() {
console.log('did not expect install success');
window.domAutomationController.send(false);
},
function(error) {
if (error.indexOf('verified site') != -1) {
window.domAutomationController.send(true);
} else {
console.log('Unexpected error: ' + error);
window.domAutomationController.send(false);
}
});
} catch (e) {
console.log('Unexpected exception: ' + e);
window.domAutomationController.send(false);
throw e;
}
}
</script>
</body>
</html>
...@@ -12,12 +12,19 @@ ...@@ -12,12 +12,19 @@
galleryUrl + '/detail/ecglahbcnmdpdciemllbhojghbkagdje'; galleryUrl + '/detail/ecglahbcnmdpdciemllbhojghbkagdje';
try { try {
chrome.webstore.install(); chrome.webstore.install(
undefined,
function() {
window.domAutomationController.send(true);
},
function(error) {
console.log('Unexpected error: ' + error);
window.domAutomationController.send(false);
});
} catch (e) { } catch (e) {
window.domAutomationController.send(false); window.domAutomationController.send(false);
throw e; throw e;
} }
window.domAutomationController.send(true);
} }
</script> </script>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment