Commit 8915f340 authored by mihaip@chromium.org's avatar mihaip@chromium.org

Add WebstoreInlineInstaller (downloads store data, shows the install UI, and starts the install).

The flow is:
1. Fetch store metadata as JSON (using URLFetcher)
2. Parse response in utility process (via SafeWebstoreResponseParser)
3. Parse manifest and get icon data using WebstoreInstallHelper
4. Show install UI
5. Whitelist extension ID for download and start download

Still missing are a way of informing the page that the inline install succeeded
or failed.

Also removes ExtensionTabHelper::GetCustomFrameNativeWindow, since it wasn't
actually overriding anything (the method was removed from
ExtensionFunctionDispatcher::Delegate by r74835).

R=asargent@chromium.org

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98712 0039d316-1c4b-4281-b951-d872f2087c98
parent 82c7bfb8
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_install_dialog.h"
#include "base/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/common/extensions/extension.h"
// A flag used for SetExtensionInstallDialogForManifestAutoConfirmForTests
enum AutoConfirmForTest {
DO_NOT_SKIP = 0,
PROCEED,
ABORT
};
AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
void ShowExtensionInstallDialogForManifest(
Profile *profile,
ExtensionInstallUI::Delegate* delegate,
const DictionaryValue* manifest,
const std::string& id,
const std::string& localized_name,
SkBitmap* icon,
ExtensionInstallUI::PromptType type,
scoped_refptr<Extension>* dummy_extension) {
scoped_ptr<DictionaryValue> localized_manifest;
if (!localized_name.empty()) {
localized_manifest.reset(manifest->DeepCopy());
localized_manifest->SetString(extension_manifest_keys::kName,
localized_name);
}
std::string init_errors;
*dummy_extension = Extension::CreateWithId(
FilePath(),
Extension::INTERNAL,
localized_manifest.get() ? *localized_manifest.get() : *manifest,
Extension::NO_FLAGS,
id,
&init_errors);
if (!dummy_extension->get()) {
return;
}
if (icon->empty())
icon = const_cast<SkBitmap*>(&Extension::GetDefaultIcon(
(*dummy_extension)->is_app()));
// In tests, we may have setup to proceed or abort without putting up the real
// confirmation dialog.
if (auto_confirm_for_tests != DO_NOT_SKIP) {
if (auto_confirm_for_tests == PROCEED)
delegate->InstallUIProceed();
else
delegate->InstallUIAbort(true);
return;
}
ShowExtensionInstallDialog(profile,
delegate,
dummy_extension->get(),
icon,
(*dummy_extension)->GetPermissionMessageStrings(),
ExtensionInstallUI::INSTALL_PROMPT);
return;
}
void SetExtensionInstallDialogForManifestAutoConfirmForTests(
bool should_proceed) {
auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "base/memory/ref_counted.h"
#include "base/string16.h" #include "base/string16.h"
#include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_install_ui.h"
...@@ -15,6 +16,10 @@ class Extension; ...@@ -15,6 +16,10 @@ class Extension;
class Profile; class Profile;
class SkBitmap; class SkBitmap;
namespace base {
class DictionaryValue;
}
// The implementations of this function are platform-specific. // The implementations of this function are platform-specific.
void ShowExtensionInstallDialog(Profile* profile, void ShowExtensionInstallDialog(Profile* profile,
ExtensionInstallUI::Delegate* delegate, ExtensionInstallUI::Delegate* delegate,
...@@ -23,4 +28,25 @@ void ShowExtensionInstallDialog(Profile* profile, ...@@ -23,4 +28,25 @@ void ShowExtensionInstallDialog(Profile* profile,
const std::vector<string16>& permissions, const std::vector<string16>& permissions,
ExtensionInstallUI::PromptType type); ExtensionInstallUI::PromptType type);
// Wrapper around ShowExtensionInstallDialog that shows the install dialog for
// a given manifest (that corresponds to an extension about to be installed with
// ID |id|). If the name in the manifest is a localized placeholder, it may be
// overidden with |localized_name| (which may be empty). The Extension instance
// that's parsed is returned via |dummy_extension|.
void ShowExtensionInstallDialogForManifest(
Profile *profile,
ExtensionInstallUI::Delegate* delegate,
const base::DictionaryValue* manifest,
const std::string& id,
const std::string& localized_name,
SkBitmap* icon,
ExtensionInstallUI::PromptType type,
scoped_refptr<Extension>* dummy_extension);
// For use only in tests - sets a flag that makes invocations of
// ShowExtensionInstallDialogForManifest skip putting up a real dialog, and
// instead act as if the dialog choice was to proceed or abort.
void SetExtensionInstallDialogForManifestAutoConfirmForTests(
bool should_proceed);
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_DIALOG_H_ #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_DIALOG_H_
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/webstore_inline_installer.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/restore_tab_helper.h" #include "chrome/browser/sessions/restore_tab_helper.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
...@@ -154,15 +155,9 @@ void ExtensionTabHelper::OnInlineWebstoreInstall( ...@@ -154,15 +155,9 @@ void ExtensionTabHelper::OnInlineWebstoreInstall(
return; return;
} }
// For now there is no inline installation UI, we just open the item's Web scoped_refptr<WebstoreInlineInstaller> installer(new WebstoreInlineInstaller(
// Store page in a new tab. tab_contents(), webstore_item_id, this));
GURL webstore_item_url = installer->BeginInstall();
GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + webstore_item_id);
GetBrowser()->OpenURL(OpenURLParams(
webstore_item_url,
GetBrowser()->GetSelectedTabContents()->GetURL(),
NEW_FOREGROUND_TAB,
PageTransition::AUTO_BOOKMARK));
} }
void ExtensionTabHelper::OnRequest( void ExtensionTabHelper::OnRequest(
...@@ -213,22 +208,17 @@ Browser* ExtensionTabHelper::GetBrowser() { ...@@ -213,22 +208,17 @@ Browser* ExtensionTabHelper::GetBrowser() {
return NULL; return NULL;
} }
TabContents* ExtensionTabHelper::GetAssociatedTabContents() const { void ExtensionTabHelper::OnInlineInstallSuccess() {
return tab_contents(); Send(new ExtensionMsg_InlineWebstoreInstallResponse(routing_id(), true, ""));
} }
gfx::NativeWindow ExtensionTabHelper::GetCustomFrameNativeWindow() { void ExtensionTabHelper::OnInlineInstallFailure(const std::string& error) {
if (GetBrowser()) Send(new ExtensionMsg_InlineWebstoreInstallResponse(
return NULL; routing_id(), false, error));
}
// If there was no browser associated with the function dispatcher delegate, TabContents* ExtensionTabHelper::GetAssociatedTabContents() const {
// then this WebUI may be hosted in an ExternalTabContainer, and a framing return tab_contents();
// window will be accessible through the tab_contents.
TabContentsDelegate* tab_contents_delegate = tab_contents()->delegate();
if (tab_contents_delegate)
return tab_contents_delegate->GetFrameNativeWindow();
else
return NULL;
} }
gfx::NativeView ExtensionTabHelper::GetNativeViewOfHost() { gfx::NativeView ExtensionTabHelper::GetNativeViewOfHost() {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "content/browser/tab_contents/tab_contents_observer.h" #include "content/browser/tab_contents/tab_contents_observer.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/extensions/image_loading_tracker.h" #include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/browser/extensions/webstore_inline_installer.h"
#include "chrome/common/web_apps.h" #include "chrome/common/web_apps.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
...@@ -23,7 +24,8 @@ struct LoadCommittedDetails; ...@@ -23,7 +24,8 @@ struct LoadCommittedDetails;
// Per-tab extension helper. Also handles non-extension apps. // Per-tab extension helper. Also handles non-extension apps.
class ExtensionTabHelper : public TabContentsObserver, class ExtensionTabHelper : public TabContentsObserver,
public ExtensionFunctionDispatcher::Delegate, public ExtensionFunctionDispatcher::Delegate,
public ImageLoadingTracker::Observer { public ImageLoadingTracker::Observer,
public WebstoreInlineInstaller::Delegate {
public: public:
explicit ExtensionTabHelper(TabContentsWrapper* wrapper); explicit ExtensionTabHelper(TabContentsWrapper* wrapper);
virtual ~ExtensionTabHelper(); virtual ~ExtensionTabHelper();
...@@ -85,13 +87,12 @@ class ExtensionTabHelper : public TabContentsObserver, ...@@ -85,13 +87,12 @@ class ExtensionTabHelper : public TabContentsObserver,
virtual void DidNavigateMainFramePostCommit( virtual void DidNavigateMainFramePostCommit(
const content::LoadCommittedDetails& details, const content::LoadCommittedDetails& details,
const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE; const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message); virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// ExtensionFunctionDispatcher::Delegate overrides. // ExtensionFunctionDispatcher::Delegate overrides.
virtual Browser* GetBrowser(); virtual Browser* GetBrowser() OVERRIDE;
virtual gfx::NativeView GetNativeViewOfHost(); virtual gfx::NativeView GetNativeViewOfHost() OVERRIDE;
virtual gfx::NativeWindow GetCustomFrameNativeWindow(); virtual TabContents* GetAssociatedTabContents() const OVERRIDE;
virtual TabContents* GetAssociatedTabContents() const;
// Message handlers. // Message handlers.
void OnDidGetApplicationInfo(int32 page_id, const WebApplicationInfo& info); void OnDidGetApplicationInfo(int32 page_id, const WebApplicationInfo& info);
...@@ -107,7 +108,11 @@ class ExtensionTabHelper : public TabContentsObserver, ...@@ -107,7 +108,11 @@ class ExtensionTabHelper : public TabContentsObserver,
// ImageLoadingTracker::Observer. // ImageLoadingTracker::Observer.
virtual void OnImageLoaded(SkBitmap* image, const ExtensionResource& resource, virtual void OnImageLoaded(SkBitmap* image, const ExtensionResource& resource,
int index); int index) OVERRIDE;
// WebstoreInlineInstaller::Delegate.
virtual void OnInlineInstallSuccess() OVERRIDE;
virtual void OnInlineInstallFailure(const std::string& error) OVERRIDE;
// Data for app extensions --------------------------------------------------- // Data for app extensions ---------------------------------------------------
......
...@@ -57,15 +57,6 @@ const char kUserGestureRequiredError[] = ...@@ -57,15 +57,6 @@ const char kUserGestureRequiredError[] =
ProfileSyncService* test_sync_service = NULL; ProfileSyncService* test_sync_service = NULL;
bool ignore_user_gesture_for_tests = false; bool ignore_user_gesture_for_tests = false;
// A flag used for BeginInstallWithManifest::SetAutoConfirmForTests.
enum AutoConfirmForTest {
DO_NOT_SKIP = 0,
PROCEED,
ABORT
};
AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
// Returns either the test sync service, or the real one from |profile|. // Returns either the test sync service, or the real one from |profile|.
ProfileSyncService* GetSyncService(Profile* profile) { ProfileSyncService* GetSyncService(Profile* profile) {
if (test_sync_service) if (test_sync_service)
...@@ -259,62 +250,27 @@ void BeginInstallWithManifestFunction::SetIgnoreUserGestureForTests( ...@@ -259,62 +250,27 @@ void BeginInstallWithManifestFunction::SetIgnoreUserGestureForTests(
ignore_user_gesture_for_tests = ignore; ignore_user_gesture_for_tests = ignore;
} }
void BeginInstallWithManifestFunction::SetAutoConfirmForTests(
bool should_proceed) {
auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
}
void BeginInstallWithManifestFunction::OnWebstoreParseSuccess( void BeginInstallWithManifestFunction::OnWebstoreParseSuccess(
const SkBitmap& icon, DictionaryValue* parsed_manifest) { const SkBitmap& icon, DictionaryValue* parsed_manifest) {
CHECK(parsed_manifest); CHECK(parsed_manifest);
icon_ = icon; icon_ = icon;
parsed_manifest_.reset(parsed_manifest); parsed_manifest_.reset(parsed_manifest);
// If we were passed a localized name to use in the dialog, create a copy ShowExtensionInstallDialogForManifest(
// of the original manifest and replace the name in it. profile(),
scoped_ptr<DictionaryValue> localized_manifest; this,
if (!localized_name_.empty()) { parsed_manifest,
localized_manifest.reset(parsed_manifest->DeepCopy());
localized_manifest->SetString(extension_manifest_keys::kName,
localized_name_);
}
// Create a dummy extension and show the extension install confirmation
// dialog.
std::string init_errors;
dummy_extension_ = Extension::CreateWithId(
FilePath(),
Extension::INTERNAL,
localized_manifest.get() ? *localized_manifest.get() : *parsed_manifest,
Extension::NO_FLAGS,
id_, id_,
&init_errors); localized_name_,
&icon_,
ExtensionInstallUI::INSTALL_PROMPT,
&dummy_extension_);
if (!dummy_extension_.get()) { if (!dummy_extension_.get()) {
OnWebstoreParseFailure(WebstoreInstallHelper::Delegate::MANIFEST_ERROR, OnWebstoreParseFailure(WebstoreInstallHelper::Delegate::MANIFEST_ERROR,
kInvalidManifestError); kInvalidManifestError);
return; return;
} }
if (icon_.empty())
icon_ = Extension::GetDefaultIcon(dummy_extension_->is_app());
// In tests, we may have setup to proceed or abort without putting up the real
// confirmation dialog.
if (auto_confirm_for_tests != DO_NOT_SKIP) {
if (auto_confirm_for_tests == PROCEED)
this->InstallUIProceed();
else
this->InstallUIAbort(true);
return;
}
ShowExtensionInstallDialog(profile(),
this,
dummy_extension_.get(),
&icon_,
dummy_extension_->GetPermissionMessageStrings(),
ExtensionInstallUI::INSTALL_PROMPT);
// Control flow finishes up in InstallUIProceed or InstallUIAbort. // Control flow finishes up in InstallUIProceed or InstallUIAbort.
} }
......
...@@ -78,11 +78,6 @@ class BeginInstallWithManifestFunction ...@@ -78,11 +78,6 @@ class BeginInstallWithManifestFunction
// the normal requirement that it is called during a user gesture. // the normal requirement that it is called during a user gesture.
static void SetIgnoreUserGestureForTests(bool ignore); static void SetIgnoreUserGestureForTests(bool ignore);
// For use only in tests - sets a flag that makes invocations of
// beginInstallWithManifest skip putting up a real dialog, and instead act
// as if the dialog choice was to proceed or abort.
static void SetAutoConfirmForTests(bool should_proceed);
// Implementing WebstoreInstallHelper::Delegate interface. // Implementing WebstoreInstallHelper::Delegate interface.
virtual void OnWebstoreParseSuccess( virtual void OnWebstoreParseSuccess(
const SkBitmap& icon, const SkBitmap& icon,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_install_dialog.h"
#include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_webstore_private_api.h" #include "chrome/browser/extensions/extension_webstore_private_api.h"
...@@ -32,7 +33,7 @@ class ExtensionWebstorePrivateApiTest : public ExtensionApiTest { ...@@ -32,7 +33,7 @@ class ExtensionWebstorePrivateApiTest : public ExtensionApiTest {
host_resolver()->AddRule("www.example.com", "127.0.0.1"); host_resolver()->AddRule("www.example.com", "127.0.0.1");
ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(test_server()->Start());
BeginInstallWithManifestFunction::SetIgnoreUserGestureForTests(true); BeginInstallWithManifestFunction::SetIgnoreUserGestureForTests(true);
BeginInstallWithManifestFunction::SetAutoConfirmForTests(true); SetExtensionInstallDialogForManifestAutoConfirmForTests(true);
ExtensionInstallUI::DisableFailureUIForTests(); ExtensionInstallUI::DisableFailureUIForTests();
} }
...@@ -80,7 +81,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallLocalized) { ...@@ -80,7 +81,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallLocalized) {
// Now test the case where the user cancels the confirmation dialog. // Now test the case where the user cancels the confirmation dialog.
IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallCancelled) { IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallCancelled) {
BeginInstallWithManifestFunction::SetAutoConfirmForTests(false); SetExtensionInstallDialogForManifestAutoConfirmForTests(false);
ASSERT_TRUE(RunInstallTest("cancelled.html", "extension.crx")); ASSERT_TRUE(RunInstallTest("cancelled.html", "extension.crx"));
} }
......
...@@ -3,72 +3,103 @@ ...@@ -3,72 +3,103 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/command_line.h" #include "base/command_line.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_install_dialog.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/webstore_inline_installer.h"
#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/ui_test_utils.h"
#include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents.h"
#include "content/common/content_notification_types.h" #include "content/common/content_notification_types.h"
#include "googleurl/src/gurl.h" #include "googleurl/src/gurl.h"
#include "net/base/host_port_pair.h"
#include "net/base/mock_host_resolver.h" #include "net/base/mock_host_resolver.h"
const char kWebstoreDomain[] = "cws.com";
const char kAppDomain[] = "app.com";
class WebstoreInlineInstallTest : public InProcessBrowserTest { class WebstoreInlineInstallTest : public InProcessBrowserTest {
public: public:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
EnableDOMAutomation(); EnableDOMAutomation();
// We start the test server now instead of in
// SetUpInProcessBrowserTestFixture so that we can get its port number.
ASSERT_TRUE(test_server()->Start());
InProcessBrowserTest::SetUpCommandLine(command_line); InProcessBrowserTest::SetUpCommandLine(command_line);
net::HostPortPair host_port = test_server()->host_port_pair();
test_gallery_url_ = base::StringPrintf(
"http://%s:%d/files/extensions/api_test/webstore_inline_install",
kWebstoreDomain, host_port.port());
command_line->AppendSwitchASCII( command_line->AppendSwitchASCII(
switches::kAppsGalleryURL, "http://cws.com"); switches::kAppsGalleryURL, test_gallery_url_);
GURL crx_url = GenerateTestServerUrl(kWebstoreDomain, "extension.crx");
CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kAppsGalleryUpdateURL, crx_url.spec());
command_line->AppendSwitch(switches::kEnableInlineWebstoreInstall); command_line->AppendSwitch(switches::kEnableInlineWebstoreInstall);
} }
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
host_resolver()->AddRule("cws.com", "127.0.0.1"); host_resolver()->AddRule(kWebstoreDomain, "127.0.0.1");
host_resolver()->AddRule("app.com", "127.0.0.1"); host_resolver()->AddRule(kAppDomain, "127.0.0.1");
ASSERT_TRUE(test_server()->Start());
} }
protected: protected:
GURL GetPageUrl(const std::string& page_filename) { GURL GenerateTestServerUrl(const std::string& domain,
const std::string& page_filename) {
GURL page_url = test_server()->GetURL( GURL page_url = test_server()->GetURL(
"files/extensions/api_test/webstore_inline_install/" + page_filename); "files/extensions/api_test/webstore_inline_install/" + page_filename);
GURL::Replacements replace_host; GURL::Replacements replace_host;
std::string host_str("app.com"); replace_host.SetHostStr(domain);
replace_host.SetHostStr(host_str);
return page_url.ReplaceComponents(replace_host); return page_url.ReplaceComponents(replace_host);
} }
std::string test_gallery_url_;
}; };
IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) { IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) {
ui_test_utils::NavigateToURL(browser(), GetPageUrl("install.html")); SetExtensionInstallDialogForManifestAutoConfirmForTests(true);
ui_test_utils::WindowedNotificationObserver load_signal(
chrome::NOTIFICATION_EXTENSION_LOADED,
Source<Profile>(browser()->profile()));
ui_test_utils::NavigateToURL(
browser(), GenerateTestServerUrl(kAppDomain, "install.html"));
bool result = false; bool result = false;
std::string script = StringPrintf("runTest('%s')", test_gallery_url_.c_str());
ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
browser()->GetSelectedTabContents()->render_view_host(), L"", browser()->GetSelectedTabContents()->render_view_host(), L"",
L"runTest()", &result)); UTF8ToWide(script), &result));
EXPECT_TRUE(result); EXPECT_TRUE(result);
// The "inline" UI right now is just the store entry in a new tab. load_signal.Wait();
if (browser()->tabstrip_model()->count() == 1) {
ui_test_utils::WaitForNewTab(browser());
}
TabContents* tab_contents = browser()->GetSelectedTabContents(); const Extension* extension = browser()->profile()->GetExtensionService()->
EXPECT_EQ( GetExtensionById("ecglahbcnmdpdciemllbhojghbkagdje", false);
GURL("http://cws.com/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"), EXPECT_TRUE(extension != NULL);
tab_contents->GetURL());
} }
IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, FindLink) { IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, FindLink) {
ui_test_utils::NavigateToURL(browser(), GetPageUrl("find_link.html")); ui_test_utils::NavigateToURL(
browser(), GenerateTestServerUrl(kAppDomain, "find_link.html"));
bool result = false; bool result = false;
std::string script = StringPrintf("runTest('%s')", test_gallery_url_.c_str());
ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
browser()->GetSelectedTabContents()->render_view_host(), L"", browser()->GetSelectedTabContents()->render_view_host(), L"",
L"runTest()", &result)); UTF8ToWide(script), &result));
EXPECT_TRUE(result); EXPECT_TRUE(result);
} }
This diff is collapsed.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_H_
#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_H_
#pragma once
#include <string>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/webstore_install_helper.h"
#include "content/common/url_fetcher.h"
#include "third_party/skia/include/core/SkBitmap.h"
class TabContents;
class SafeWebstoreResponseParser;
// Manages inline installs requested by a page (downloads and parses metadata
// from the webstore, shows the install UI, starts the download once the user
// confirms). Clients must implement the WebstoreInlineInstaller::Delegate
// interface to be notified when the inline install completes (successfully or
// not).
class WebstoreInlineInstaller
: public base::RefCountedThreadSafe<WebstoreInlineInstaller>,
public ExtensionInstallUI::Delegate,
public URLFetcher::Delegate,
public WebstoreInstallHelper::Delegate {
public:
class Delegate {
public:
virtual void OnInlineInstallSuccess() = 0;
virtual void OnInlineInstallFailure(const std::string& error) = 0;
};
WebstoreInlineInstaller(TabContents* tab_contents,
std::string webstore_item_id,
Delegate* d);
void BeginInstall();
private:
friend class base::RefCountedThreadSafe<WebstoreInlineInstaller>;
friend class SafeWebstoreResponseParser;
virtual ~WebstoreInlineInstaller();
// Several delegate/client inteface implementations follow. The normal flow
// (for successful installs) is:
//
// 1. BeginInstall: starts the fetch of data from the webstore
// 2. OnURLFetchComplete: starts the parsing of data from the webstore
// 3. OnWebstoreResponseParseSuccess: starts the parsing of the manifest and
// fetching of icon data.
// 4. OnWebstoreParseSuccess: shows the install UI
// 5. InstallUIProceed: initiates the .crx download/install
//
// All flows (whether successful or not) end up in CompleteInstall, which
// informs our delegate of success/failure.
// UrlFetcher::Delegate interface implementation.
virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE;
// Client callbacks for SafeWebstoreResponseParser when parsing is complete.
void OnWebstoreResponseParseSuccess(DictionaryValue* webstore_data);
void OnWebstoreResponseParseFailure(const std::string& error);
// WebstoreInstallHelper::Delegate interface implementation.
virtual void OnWebstoreParseSuccess(
const SkBitmap& icon,
base::DictionaryValue* parsed_manifest) OVERRIDE;
virtual void OnWebstoreParseFailure(
InstallHelperResultCode result_code,
const std::string& error_message) OVERRIDE;
// ExtensionInstallUI::Delegate interface implementation.
virtual void InstallUIProceed() OVERRIDE;
virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
void CompleteInstall(const std::string& error);
TabContents* tab_contents_;
std::string id_;
Delegate* delegate_;
// For fetching webstore JSON data.
scoped_ptr<URLFetcher> webstore_data_url_fetcher_;
// Extracted from the webstore JSON data response.
std::string localized_name_;
scoped_ptr<DictionaryValue> webstore_data_;
scoped_ptr<DictionaryValue> manifest_;
SkBitmap icon_;
DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreInlineInstaller);
};
#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_H_
...@@ -995,6 +995,7 @@ ...@@ -995,6 +995,7 @@
'browser/extensions/extension_input_module_constants.h', 'browser/extensions/extension_input_module_constants.h',
'browser/extensions/extension_input_ui_api.cc', 'browser/extensions/extension_input_ui_api.cc',
'browser/extensions/extension_input_ui_api.h', 'browser/extensions/extension_input_ui_api.h',
'browser/extensions/extension_install_dialog.cc',
'browser/extensions/extension_install_dialog.h', 'browser/extensions/extension_install_dialog.h',
'browser/extensions/extension_install_ui.cc', 'browser/extensions/extension_install_ui.cc',
'browser/extensions/extension_install_ui.h', 'browser/extensions/extension_install_ui.h',
...@@ -1155,6 +1156,8 @@ ...@@ -1155,6 +1156,8 @@
'browser/extensions/user_script_listener.h', 'browser/extensions/user_script_listener.h',
'browser/extensions/user_script_master.cc', 'browser/extensions/user_script_master.cc',
'browser/extensions/user_script_master.h', 'browser/extensions/user_script_master.h',
'browser/extensions/webstore_inline_installer.cc',
'browser/extensions/webstore_inline_installer.h',
'browser/extensions/webstore_install_helper.cc', 'browser/extensions/webstore_install_helper.cc',
'browser/extensions/webstore_install_helper.h', 'browser/extensions/webstore_install_helper.h',
'browser/external_protocol/external_protocol_handler.cc', 'browser/external_protocol/external_protocol_handler.cc',
......
...@@ -425,6 +425,10 @@ std::string GetWebstoreItemDetailURLPrefix() { ...@@ -425,6 +425,10 @@ std::string GetWebstoreItemDetailURLPrefix() {
return GetWebstoreLaunchURL() + "/detail/"; return GetWebstoreLaunchURL() + "/detail/";
} }
GURL GetWebstoreItemJsonDataURL(const std::string& extension_id) {
return GURL(GetWebstoreItemDetailURLPrefix() + extension_id + "?output=json");
}
const char* kGalleryUpdateHttpUrl = const char* kGalleryUpdateHttpUrl =
"http://clients2.google.com/service/update2/crx"; "http://clients2.google.com/service/update2/crx";
const char* kGalleryUpdateHttpsUrl = const char* kGalleryUpdateHttpsUrl =
......
...@@ -281,6 +281,10 @@ namespace extension_urls { ...@@ -281,6 +281,10 @@ namespace extension_urls {
// to get the item detail URL. // to get the item detail URL.
std::string GetWebstoreItemDetailURLPrefix(); std::string GetWebstoreItemDetailURLPrefix();
// Returns the URL used to get webstore data (ratings, manifest, icon URL,
// etc.) about an extension from the webstore as JSON.
GURL GetWebstoreItemJsonDataURL(const std::string& extension_id);
// Return the update URL used by gallery/webstore extensions/apps. The // Return the update URL used by gallery/webstore extensions/apps. The
// |secure| parameter will be ignored if the update URL is overriden with // |secure| parameter will be ignored if the update URL is overriden with
// --apps-gallery-update-url. // --apps-gallery-update-url.
......
...@@ -317,3 +317,8 @@ IPC_MESSAGE_ROUTED1(ExtensionHostMsg_InstallApplication, ...@@ -317,3 +317,8 @@ IPC_MESSAGE_ROUTED1(ExtensionHostMsg_InstallApplication,
// 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_ROUTED1(ExtensionHostMsg_InlineWebstoreInstall,
std::string /* Web Store item ID */) std::string /* Web Store item ID */)
// Send to renderer once the installation mentioned above is complete.
IPC_MESSAGE_ROUTED2(ExtensionMsg_InlineWebstoreInstallResponse,
bool /* whether the install was successful */,
std::string /* error */)
...@@ -107,6 +107,18 @@ void ExtensionHelper::InlineWebstoreInstall(std::string webstore_item_id) { ...@@ -107,6 +107,18 @@ void ExtensionHelper::InlineWebstoreInstall(std::string webstore_item_id) {
routing_id(), webstore_item_id)); routing_id(), webstore_item_id));
} }
void ExtensionHelper::OnInlineWebstoreInstallResponse(
bool success,
const std::string& error) {
// TODO(mihaip): dispatch these as events to the the WebFrame that initiated
// the inline install.
if (success) {
VLOG(1) << "Inline install succeeded.";
} else {
VLOG(1) << "Inline install failed: " << error;
}
}
bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) { bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) {
bool handled = true; bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ExtensionHelper, message) IPC_BEGIN_MESSAGE_MAP(ExtensionHelper, message)
...@@ -118,6 +130,8 @@ bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) { ...@@ -118,6 +130,8 @@ bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) {
OnUpdateBrowserWindowId) OnUpdateBrowserWindowId)
IPC_MESSAGE_HANDLER(ExtensionMsg_NotifyRenderViewType, IPC_MESSAGE_HANDLER(ExtensionMsg_NotifyRenderViewType,
OnNotifyRendererViewType) OnNotifyRendererViewType)
IPC_MESSAGE_HANDLER(ExtensionMsg_InlineWebstoreInstallResponse,
OnInlineWebstoreInstallResponse)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() IPC_END_MESSAGE_MAP()
return handled; return handled;
......
...@@ -68,6 +68,7 @@ class ExtensionHelper : public RenderViewObserver, ...@@ -68,6 +68,7 @@ class ExtensionHelper : public RenderViewObserver,
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);
// Callback triggered when we finish downloading the application definition // Callback triggered when we finish downloading the application definition
// file. // file.
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
} }
} }
function runTest() { function runTest(galleryUrl) {
// Definitely no link. // Definitely no link.
checkNoLinkFound(NO_LINK_EXCEPTION); checkNoLinkFound(NO_LINK_EXCEPTION);
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
// Wrong type, right URL. // Wrong type, right URL.
linkNode.rel = 'stylesheet'; linkNode.rel = 'stylesheet';
linkNode.href = 'http://cws.com/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm'; linkNode.href = galleryUrl + '/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm';
checkNoLinkFound(NO_LINK_EXCEPTION); checkNoLinkFound(NO_LINK_EXCEPTION);
// Right type, wrong URL. // Right type, wrong URL.
...@@ -42,17 +42,17 @@ ...@@ -42,17 +42,17 @@
// Non-item CWS URL // Non-item CWS URL
linkNode.href = linkNode.href =
'http://cws.com/someotherpage/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm'; galleryUrl + '/someotherpage/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm';
checkNoLinkFound(INVALID_URL_EXCEPTION); checkNoLinkFound(INVALID_URL_EXCEPTION);
// Invalid ID // Invalid ID
linkNode.rel = 'chrome-webstore-item'; linkNode.rel = 'chrome-webstore-item';
linkNode.href = 'http://cws.com/detail/abc'; linkNode.href = galleryUrl + '/detail/abc';
checkNoLinkFound(INVALID_URL_EXCEPTION); checkNoLinkFound(INVALID_URL_EXCEPTION);
// Extra CWS URL parameters // Extra CWS URL parameters
linkNode.href = linkNode.href =
'http://cws.com/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm?foo=bar'; galleryUrl + '/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm?foo=bar';
checkNoLinkFound(INVALID_URL_EXCEPTION); checkNoLinkFound(INVALID_URL_EXCEPTION);
// Successful installation is tested elsewhere // Successful installation is tested elsewhere
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<link rel="chrome-webstore-item" href="http://cws.com/detail/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"> <link rel="chrome-webstore-item">
</head> </head>
<body> <body>
<script> <script>
function runTest() { function runTest(galleryUrl) {
chrome.webstore.install(); // 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();
} catch (e) {
window.domAutomationController.send(false);
throw e;
}
window.domAutomationController.send(true); 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