summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-04 22:12:49 +0000
committerasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-04 22:12:49 +0000
commitcbe224d9ac80770b08f36035505358c397431e4b (patch)
treeec40d89290fd2deb5764c04d0d881880e3cab1c3
parent52e3f6aeeb805db5051f33f9fdf79671dbe839bc (diff)
downloadchromium_src-cbe224d9ac80770b08f36035505358c397431e4b.zip
chromium_src-cbe224d9ac80770b08f36035505358c397431e4b.tar.gz
chromium_src-cbe224d9ac80770b08f36035505358c397431e4b.tar.bz2
Add a flag that lets the webstore show a different UI on app install.
When people install apps, they seem to get confused about how to launch them. We want to experiment with a different UI after install, that instead of immediately transitioning to the New Tab Page, instead shows a bubble pointing at the New Tab button on the tabstrip with a "show me" link which will open a new tab and animate the app icon showing up there. This CL has the views implementation - OSX and GTK implementations will come in a separate CL. BUG=89687 TEST=Requires webstore changes to fully test (the CL includes an automated browser test) Review URL: http://codereview.chromium.org/7529011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95514 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd10
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/extensions/crx_installer.cc5
-rw-r--r--chrome/browser/extensions/crx_installer.h4
-rw-r--r--chrome/browser/extensions/extension_install_ui.cc28
-rw-r--r--chrome/browser/extensions/extension_install_ui.h17
-rw-r--r--chrome/browser/extensions/extension_webstore_private_api.cc9
-rw-r--r--chrome/browser/extensions/extension_webstore_private_api.h1
-rw-r--r--chrome/browser/extensions/extension_webstore_private_apitest.cc7
-rw-r--r--chrome/browser/ui/touch/tabs/touch_tab_strip.cc4
-rw-r--r--chrome/browser/ui/touch/tabs/touch_tab_strip.h5
-rw-r--r--chrome/browser/ui/views/extensions/extension_installed_bubble.cc114
-rw-r--r--chrome/browser/ui/views/extensions/extension_installed_bubble.h2
-rw-r--r--chrome/browser/ui/views/tabs/abstract_tab_strip_view.h4
-rw-r--r--chrome/browser/ui/views/tabs/side_tab_strip.cc4
-rw-r--r--chrome/browser/ui/views/tabs/side_tab_strip.h1
-rw-r--r--chrome/browser/ui/views/tabs/tab_strip.cc4
-rw-r--r--chrome/browser/ui/views/tabs/tab_strip.h1
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h3
-rw-r--r--chrome/common/extensions/api/extension_api.json5
-rw-r--r--chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.html2
-rw-r--r--chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.js25
-rw-r--r--chrome/test/data/extensions/api_test/webstore_private/common.js5
24 files changed, 225 insertions, 45 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 434a4ca..6570e4d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3879,6 +3879,10 @@ are declared in build/common.gypi.
<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph> is now installed.
</message>
+ <message name="IDS_EXTENSION_INSTALLED_APP_INFO" desc="Text displayed inside a link when an app is installed. Clicking this link opens up the New Tab Page to show the app's icon.">
+ Show me
+ </message>
+
<message name="IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO" desc="Text displayed in the InfoBubble which explains that the UI of this extension is a Page Action icon which may appear for some pages.">
This icon will be visible when the extension can act on the current page.
</message>
@@ -4310,6 +4314,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION" desc="Description of the 'Experimental Extension APIs' lab.">
Enables experimental extension APIs. Note that the extension gallery doesn't allow you to upload extensions that use experimental APIs.
</message>
+ <message name="IDS_FLAGS_APPS_NEW_INSTALL_BUBBLE_NAME" desc="Name of the 'New Apps Install Bubble' lab">
+ New Apps Install Bubble
+ </message>
+ <message name="IDS_FLAGS_APPS_NEW_INSTALL_BUBBLE_DESCRIPTION" desc="Description of the 'New Apps Install Bubble' lab">
+ When installing an app, always show a bubble pointing at the new tab page button on the tabstrip instead of opening a new tab page.
+ </message>
<message name="IDS_FLAGS_CLICK_TO_PLAY_NAME" desc="Name of the 'Click to play' lab.">
Click to play
</message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5181032..2828e70 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -188,6 +188,13 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(switches::kEnableExperimentalExtensionApis)
},
{
+ "apps-new-install-bubble",
+ IDS_FLAGS_APPS_NEW_INSTALL_BUBBLE_NAME,
+ IDS_FLAGS_APPS_NEW_INSTALL_BUBBLE_DESCRIPTION,
+ kOsAll,
+ SINGLE_VALUE_TYPE(switches::kAppsNewInstallBubble)
+ },
+ {
"click-to-play", // FLAGS:RECORD_UMA
IDS_FLAGS_CLICK_TO_PLAY_NAME,
IDS_FLAGS_CLICK_TO_PLAY_DESCRIPTION,
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index c28c5e4..e5a008f 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -52,7 +52,8 @@ static base::LazyInstance<Whitelist>
} // namespace
-CrxInstaller::WhitelistEntry::WhitelistEntry() {}
+CrxInstaller::WhitelistEntry::WhitelistEntry()
+ : use_app_installed_bubble(false) {}
CrxInstaller::WhitelistEntry::~WhitelistEntry() {}
// static
@@ -406,6 +407,8 @@ void CrxInstaller::ConfirmInstall() {
return;
}
whitelisted = true;
+ if (entry->use_app_installed_bubble)
+ client_->set_use_app_installed_bubble(true);
}
if (client_ &&
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 171105b..1db8104 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -61,6 +61,10 @@ class CrxInstaller
scoped_ptr<base::DictionaryValue> parsed_manifest;
std::string localized_name;
+
+ // Whether to use a bubble notification when an app is installed, instead of
+ // the default behavior of transitioning to the new tab page.
+ bool use_app_installed_bubble;
};
// Exempt the next extension install with |id| from displaying a confirmation
diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc
index bdbd839..dc9a6a90 100644
--- a/chrome/browser/extensions/extension_install_ui.cc
+++ b/chrome/browser/extensions/extension_install_ui.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_icon_set.h"
#include "chrome/common/extensions/extension_resource.h"
@@ -88,10 +89,8 @@ void ShowAppInstalledAnimation(Browser* browser, const std::string& app_id) {
}
}
- // If there isn't an NTP, open one and pass it the ID of the installed app.
- std::string url = base::StringPrintf(
- "%s#app-id=%s", chrome::kChromeUINewTabURL, app_id.c_str());
- browser->AddSelectedTabWithURL(GURL(url), PageTransition::TYPED);
+ // If there isn't an NTP, open one.
+ ExtensionInstallUI::OpenAppInstalledNTP(browser, app_id);
}
} // namespace
@@ -103,7 +102,8 @@ ExtensionInstallUI::ExtensionInstallUI(Profile* profile)
extension_(NULL),
delegate_(NULL),
prompt_type_(NUM_PROMPT_TYPES),
- ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) {
+ ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)),
+ use_app_installed_bubble_(false) {
// Remember the current theme in case the user presses undo.
if (profile_) {
const Extension* previous_theme =
@@ -177,7 +177,15 @@ void ExtensionInstallUI::OnInstallSuccess(const Extension* extension,
browser->AddBlankTab(true);
browser->window()->Show();
- if (extension->GetFullLaunchURL().is_valid()) {
+ bool use_bubble_for_apps = false;
+
+#if defined(TOOLKIT_VIEWS)
+ CommandLine* cmdline = CommandLine::ForCurrentProcess();
+ use_bubble_for_apps = (use_app_installed_bubble_ ||
+ cmdline->HasSwitch(switches::kAppsNewInstallBubble));
+#endif
+
+ if (extension->is_app() && !use_bubble_for_apps) {
ShowAppInstalledAnimation(browser, extension->id());
return;
}
@@ -240,6 +248,14 @@ void ExtensionInstallUI::OnImageLoaded(
}
// static
+void ExtensionInstallUI::OpenAppInstalledNTP(Browser* browser,
+ const std::string& app_id) {
+ std::string url = base::StringPrintf(
+ "%s#app-id=%s", chrome::kChromeUINewTabURL, app_id.c_str());
+ browser->AddSelectedTabWithURL(GURL(url), PageTransition::TYPED);
+}
+
+// static
void ExtensionInstallUI::DisableFailureUIForTests() {
disable_failure_ui_for_tests = true;
}
diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h
index 19965e5..4ce5720 100644
--- a/chrome/browser/extensions/extension_install_ui.h
+++ b/chrome/browser/extensions/extension_install_ui.h
@@ -15,6 +15,7 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/native_widget_types.h"
+class Browser;
class Extension;
class ExtensionPermissionSet;
class MessageLoop;
@@ -56,6 +57,14 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
explicit ExtensionInstallUI(Profile* profile);
virtual ~ExtensionInstallUI();
+ // TODO(asargent) Normally we navigate to the new tab page when an app is
+ // installed, but we're experimenting with instead showing a bubble when
+ // an app is installed which points to the new tab button. This may become
+ // the default behavior in the future.
+ void set_use_app_installed_bubble(bool use_bubble) {
+ use_app_installed_bubble_ = use_bubble;
+ }
+
// This is called by the installer to verify whether the installation should
// proceed. This is declared virtual for testing.
//
@@ -86,6 +95,10 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
virtual void OnImageLoaded(
SkBitmap* image, const ExtensionResource& resource, int index);
+ // Opens a new tab page and animates the app icon for the app with id
+ // |app_id|.
+ static void OpenAppInstalledNTP(Browser* browser, const std::string& app_id);
+
protected:
friend class ExtensionWebstorePrivateApiTest;
@@ -145,6 +158,10 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
// Keeps track of extension images being loaded on the File thread for the
// purpose of showing the install UI.
ImageLoadingTracker tracker_;
+
+ // Whether to show an installed bubble on app install, or use the default
+ // action of opening a new tab page.
+ bool use_app_installed_bubble_;
};
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_UI_H_
diff --git a/chrome/browser/extensions/extension_webstore_private_api.cc b/chrome/browser/extensions/extension_webstore_private_api.cc
index 8276e81..0f56c34 100644
--- a/chrome/browser/extensions/extension_webstore_private_api.cc
+++ b/chrome/browser/extensions/extension_webstore_private_api.cc
@@ -37,6 +37,7 @@
namespace {
+const char kAppInstallBubbleKey[] = "appInstallBubble";
const char kIconDataKey[] = "iconData";
const char kIdKey[] = "id";
const char kLocalizedNameKey[] = "localizedName";
@@ -293,7 +294,8 @@ class SafeBeginInstallHelper : public UtilityProcessHost::Client {
BeginInstallWithManifestFunction::ResultCode parse_error_;
};
-BeginInstallWithManifestFunction::BeginInstallWithManifestFunction() {}
+BeginInstallWithManifestFunction::BeginInstallWithManifestFunction()
+ : use_app_installed_bubble_(false) {}
BeginInstallWithManifestFunction::~BeginInstallWithManifestFunction() {}
@@ -329,6 +331,10 @@ bool BeginInstallWithManifestFunction::RunImpl() {
EXTENSION_FUNCTION_VALIDATE(details->GetString(kLocalizedNameKey,
&localized_name_));
+ if (details->HasKey(kAppInstallBubbleKey))
+ EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(
+ kAppInstallBubbleKey, &use_app_installed_bubble_));
+
scoped_refptr<SafeBeginInstallHelper> helper =
new SafeBeginInstallHelper(this, icon_data_, manifest_);
// The helper will call us back via OnParseSucces or OnParseFailure.
@@ -451,6 +457,7 @@ void BeginInstallWithManifestFunction::InstallUIProceed() {
CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry;
entry->parsed_manifest.reset(parsed_manifest_.release());
entry->localized_name = localized_name_;
+ entry->use_app_installed_bubble = use_app_installed_bubble_;
CrxInstaller::SetWhitelistEntry(id_, entry);
SetResult(ERROR_NONE);
SendResponse(true);
diff --git a/chrome/browser/extensions/extension_webstore_private_api.h b/chrome/browser/extensions/extension_webstore_private_api.h
index 19eec7a..081e6b7 100644
--- a/chrome/browser/extensions/extension_webstore_private_api.h
+++ b/chrome/browser/extensions/extension_webstore_private_api.h
@@ -108,6 +108,7 @@ class BeginInstallWithManifestFunction : public AsyncExtensionFunction,
std::string manifest_;
std::string icon_data_;
std::string localized_name_;
+ bool use_app_installed_bubble_;
// The results of parsing manifest_ and icon_data_ go into these two.
scoped_ptr<base::DictionaryValue> parsed_manifest_;
diff --git a/chrome/browser/extensions/extension_webstore_private_apitest.cc b/chrome/browser/extensions/extension_webstore_private_apitest.cc
index 88e772c..68be872 100644
--- a/chrome/browser/extensions/extension_webstore_private_apitest.cc
+++ b/chrome/browser/extensions/extension_webstore_private_apitest.cc
@@ -106,3 +106,10 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest,
ASSERT_TRUE(RunInstallTest("incorrect_manifest2.html", "extension.crx"));
observer.Wait();
}
+
+// Tests that we can request an app installed bubble (instead of the default
+// UI when an app is installed).
+IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest,
+ AppInstallBubble) {
+ ASSERT_TRUE(RunInstallTest("app_install_bubble.html", "app.crx"));
+}
diff --git a/chrome/browser/ui/touch/tabs/touch_tab_strip.cc b/chrome/browser/ui/touch/tabs/touch_tab_strip.cc
index 6d7bf25..c4e3240 100644
--- a/chrome/browser/ui/touch/tabs/touch_tab_strip.cc
+++ b/chrome/browser/ui/touch/tabs/touch_tab_strip.cc
@@ -59,6 +59,10 @@ void TouchTabStrip::SetBackgroundOffset(const gfx::Point& offset) {
GetTabAtTabDataIndex(i)->set_background_offset(offset);
}
+views::View* TouchTabStrip::GetNewTabButton() {
+ return NULL;
+}
+
////////////////////////////////////////////////////////////////////////////////
// TouchTabStrip, BaseTabStrip implementation:
diff --git a/chrome/browser/ui/touch/tabs/touch_tab_strip.h b/chrome/browser/ui/touch/tabs/touch_tab_strip.h
index cf4c686..1ecaa68 100644
--- a/chrome/browser/ui/touch/tabs/touch_tab_strip.h
+++ b/chrome/browser/ui/touch/tabs/touch_tab_strip.h
@@ -32,8 +32,9 @@ class TouchTabStrip : public BaseTabStrip {
virtual ~TouchTabStrip();
// AbstractTabStripView implementation:
- virtual bool IsPositionInWindowCaption(const gfx::Point& point);
- virtual void SetBackgroundOffset(const gfx::Point& offset);
+ virtual bool IsPositionInWindowCaption(const gfx::Point& point) OVERRIDE;
+ virtual void SetBackgroundOffset(const gfx::Point& offset) OVERRIDE;
+ virtual views::View* GetNewTabButton() OVERRIDE;
// BaseTabStrip implementation:
virtual void PrepareForCloseAt(int model_index);
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
index f9e30ab..5e0ff1e 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
@@ -9,12 +9,14 @@
#include "base/i18n/rtl.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/browser_actions_container.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/toolbar_view.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
@@ -28,6 +30,8 @@
#include "views/controls/button/image_button.h"
#include "views/controls/image_view.h"
#include "views/controls/label.h"
+#include "views/controls/link.h"
+#include "views/controls/link_listener.h"
#include "views/layout/layout_constants.h"
#include "views/view.h"
@@ -78,12 +82,16 @@ void ShowExtensionInstalledBubble(
// ExtensionInstalledBubble. It displays the install icon and explanatory
// text about the installed extension.
class InstalledBubbleContent : public views::View,
- public views::ButtonListener {
+ public views::ButtonListener,
+ public views::LinkListener {
public:
- InstalledBubbleContent(const Extension* extension,
+ InstalledBubbleContent(Browser* browser,
+ const Extension* extension,
ExtensionInstalledBubble::BubbleType type,
SkBitmap* icon)
- : bubble_(NULL),
+ : browser_(browser),
+ extension_id_(extension->id()),
+ bubble_(NULL),
type_(type),
info_(NULL) {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
@@ -108,32 +116,50 @@ class InstalledBubbleContent : public views::View,
heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
AddChildView(heading_);
- if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
- info_ = new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
- IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO)));
- info_->SetFont(font);
- info_->SetMultiLine(true);
- info_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
- AddChildView(info_);
+ switch (type_) {
+ case ExtensionInstalledBubble::PAGE_ACTION: {
+ info_ = new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
+ IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO)));
+ info_->SetFont(font);
+ info_->SetMultiLine(true);
+ info_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(info_);
+ break;
+ }
+ case ExtensionInstalledBubble::OMNIBOX_KEYWORD: {
+ info_ = new views::Label(UTF16ToWide(l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO,
+ UTF8ToUTF16(extension->omnibox_keyword()))));
+ info_->SetFont(font);
+ info_->SetMultiLine(true);
+ info_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(info_);
+ break;
+ }
+ case ExtensionInstalledBubble::APP: {
+ views::Link* link = new views::Link(UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_APP_INFO)));
+ link->set_listener(this);
+ manage_ = link;
+ manage_->SetFont(font);
+ manage_->SetMultiLine(true);
+ manage_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(manage_);
+ break;
+ }
+ default:
+ break;
}
- if (type_ == ExtensionInstalledBubble::OMNIBOX_KEYWORD) {
- info_ = new views::Label(UTF16ToWide(l10n_util::GetStringFUTF16(
- IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO,
- UTF8ToUTF16(extension->omnibox_keyword()))));
- info_->SetFont(font);
- info_->SetMultiLine(true);
- info_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
- AddChildView(info_);
+ if (type_ != ExtensionInstalledBubble::APP) {
+ manage_ = new views::Label(UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_INFO)));
+ manage_->SetFont(font);
+ manage_->SetMultiLine(true);
+ manage_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(manage_);
}
- manage_ = new views::Label(UTF16ToWide(
- l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_INFO)));
- manage_->SetFont(font);
- manage_->SetMultiLine(true);
- manage_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
- AddChildView(manage_);
-
close_button_ = new views::ImageButton(this);
close_button_->SetImage(views::CustomButton::BS_NORMAL,
rb.GetBitmapNamed(IDR_CLOSE_BAR));
@@ -157,6 +183,12 @@ class InstalledBubbleContent : public views::View,
}
}
+ // Implements the views::LinkListener interface.
+ virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
+ GetWidget()->Close();
+ ExtensionInstallUI::OpenAppInstalledNTP(browser_, extension_id_);
+ }
+
private:
virtual gfx::Size GetPreferredSize() {
int width = kHorizOuterMargin;
@@ -169,8 +201,7 @@ class InstalledBubbleContent : public views::View,
int height = kVertOuterMargin;
height += heading_->GetHeightForWidth(kRightColumnWidth);
height += kVertInnerMargin;
- if (type_ == ExtensionInstalledBubble::PAGE_ACTION ||
- type_ == ExtensionInstalledBubble::OMNIBOX_KEYWORD) {
+ if (info_) {
height += info_->GetHeightForWidth(kRightColumnWidth);
height += kVertInnerMargin;
}
@@ -195,8 +226,7 @@ class InstalledBubbleContent : public views::View,
y += heading_->height();
y += kVertInnerMargin;
- if (type_ == ExtensionInstalledBubble::PAGE_ACTION ||
- type_ == ExtensionInstalledBubble::OMNIBOX_KEYWORD) {
+ if (info_) {
info_->SizeToFit(kRightColumnWidth);
info_->SetX(x);
info_->SetY(y);
@@ -220,6 +250,12 @@ class InstalledBubbleContent : public views::View,
close_button_->SetBounds(x - 1, y - 1, sz.width(), sz.height());
}
+ // The browser we're associated with.
+ Browser* browser_;
+
+ // The id of the extension just installed.
+ const std::string extension_id_;
+
// The Bubble showing us.
Bubble* bubble_;
@@ -248,7 +284,9 @@ ExtensionInstalledBubble::ExtensionInstalledBubble(const Extension* extension,
animation_wait_retries_(0) {
AddRef(); // Balanced in BubbleClosing.
- if (!extension_->omnibox_keyword().empty()) {
+ if (extension->is_app()) {
+ type_ = APP;
+ } else if (!extension_->omnibox_keyword().empty()) {
type_ = OMNIBOX_KEYWORD;
} else if (extension_->browser_action()) {
type_ = BROWSER_ACTION;
@@ -298,7 +336,18 @@ void ExtensionInstalledBubble::ShowInternal() {
browser_->window()->GetNativeHandle());
const views::View* reference_view = NULL;
- if (type_ == BROWSER_ACTION) {
+ if (type_ == APP) {
+ if (browser_view->IsTabStripVisible()) {
+ AbstractTabStripView* tabstrip = browser_view->tabstrip();
+ views::View* ntp_button = tabstrip->GetNewTabButton();
+ if (ntp_button && ntp_button->IsVisibleInRootView()) {
+ reference_view = ntp_button;
+ } else {
+ // Just have the bubble point at the tab strip.
+ reference_view = tabstrip;
+ }
+ }
+ } else if (type_ == BROWSER_ACTION) {
BrowserActionsContainer* container =
browser_view->GetToolbarView()->browser_actions();
if (container->animating() &&
@@ -352,7 +401,8 @@ void ExtensionInstalledBubble::ShowInternal() {
arrow_location = BubbleBorder::TOP_LEFT;
}
- bubble_content_ = new InstalledBubbleContent(extension_, type_, &icon_);
+ bubble_content_ = new InstalledBubbleContent(
+ browser_, extension_, type_, &icon_);
Bubble* bubble = Bubble::Show(browser_view->GetWidget(), bounds,
arrow_location, bubble_content_, this);
bubble_content_->set_bubble(bubble);
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble.h b/chrome/browser/ui/views/extensions/extension_installed_bubble.h
index 8f5f5b7..887a2df 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble.h
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble.h
@@ -24,6 +24,7 @@ class SkBitmap;
// BROWSER_ACTION -> The browserAction icon in the toolbar.
// PAGE_ACTION -> A preview of the pageAction icon in the location
// bar which is shown while the Bubble is shown.
+// APP -> The plus button in the tabstrip (for the New Tab Page).
// GENERIC -> The wrench menu. This case includes pageActions that
// don't specify a default icon.
//
@@ -38,6 +39,7 @@ class ExtensionInstalledBubble
OMNIBOX_KEYWORD,
BROWSER_ACTION,
PAGE_ACTION,
+ APP,
GENERIC
};
diff --git a/chrome/browser/ui/views/tabs/abstract_tab_strip_view.h b/chrome/browser/ui/views/tabs/abstract_tab_strip_view.h
index 756680a..1ce1814 100644
--- a/chrome/browser/ui/views/tabs/abstract_tab_strip_view.h
+++ b/chrome/browser/ui/views/tabs/abstract_tab_strip_view.h
@@ -32,7 +32,9 @@ class AbstractTabStripView : public views::View {
// Set the background offset used by inactive tabs to match the frame image.
virtual void SetBackgroundOffset(const gfx::Point& offset) = 0;
+
+ // Returns the new tab button, or NULL if there isn't one.
+ virtual views::View* GetNewTabButton() = 0;
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_ABSTRACT_TAB_STRIP_VIEW_H_
-
diff --git a/chrome/browser/ui/views/tabs/side_tab_strip.cc b/chrome/browser/ui/views/tabs/side_tab_strip.cc
index f7edbfa..82c457f 100644
--- a/chrome/browser/ui/views/tabs/side_tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/side_tab_strip.cc
@@ -155,6 +155,10 @@ bool SideTabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
void SideTabStrip::SetBackgroundOffset(const gfx::Point& offset) {
}
+views::View* SideTabStrip::GetNewTabButton() {
+ return newtab_button_;
+}
+
////////////////////////////////////////////////////////////////////////////////
// SideTabStrip, BaseTabStrip implementation:
diff --git a/chrome/browser/ui/views/tabs/side_tab_strip.h b/chrome/browser/ui/views/tabs/side_tab_strip.h
index 6a5ef7e..9fc316f 100644
--- a/chrome/browser/ui/views/tabs/side_tab_strip.h
+++ b/chrome/browser/ui/views/tabs/side_tab_strip.h
@@ -23,6 +23,7 @@ class SideTabStrip : public BaseTabStrip, public views::ButtonListener {
// AbstractTabStripView implementation:
virtual bool IsPositionInWindowCaption(const gfx::Point& point) OVERRIDE;
virtual void SetBackgroundOffset(const gfx::Point& offset) OVERRIDE;
+ virtual views::View* GetNewTabButton() OVERRIDE;
// BaseTabStrip implementation:
virtual void StartHighlight(int model_index) OVERRIDE;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index ea1451a..6b6894b3 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -189,6 +189,10 @@ void TabStrip::SetBackgroundOffset(const gfx::Point& offset) {
GetTabAtTabDataIndex(i)->set_background_offset(offset);
}
+views::View* TabStrip::GetNewTabButton() {
+ return newtab_button_;
+}
+
////////////////////////////////////////////////////////////////////////////////
// TabStrip, BaseTabStrip implementation:
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index b064e9a..009cf2a 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -54,6 +54,7 @@ class TabStrip : public BaseTabStrip,
// AbstractTabStripView implementation:
virtual bool IsPositionInWindowCaption(const gfx::Point& point) OVERRIDE;
virtual void SetBackgroundOffset(const gfx::Point& offset) OVERRIDE;
+ virtual views::View* GetNewTabButton() OVERRIDE;
// BaseTabStrip implementation:
virtual void PrepareForCloseAt(int model_index) OVERRIDE;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 5f6ec9e..afaf93a 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -70,6 +70,9 @@ const char kAppsGalleryURL[] = "apps-gallery-url";
// The update url used by gallery/webstore extensions.
const char kAppsGalleryUpdateURL[] = "apps-gallery-update-url";
+// Whether to always use the new app install bubble when installing an app.
+const char kAppsNewInstallBubble[] = "apps-new-install-bubble";
+
// Disable throbber for extension apps.
const char kAppsNoThrob[] = "apps-no-throb";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 7879e04..fca7bb5 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -32,12 +32,13 @@ extern const char kAllowWebSocketProxy[];
extern const char kAllowWebUICompositing[];
extern const char kAllowWebUIOobe[];
extern const char kAlwaysAuthorizePlugins[];
-extern const char kApp[];
extern const char kAppId[];
+extern const char kApp[];
extern const char kAppsCheckoutURL[];
extern const char kAppsGalleryReturnTokens[];
extern const char kAppsGalleryURL[];
extern const char kAppsGalleryUpdateURL[];
+extern const char kAppsNewInstallBubble[];
extern const char kAppsNoThrob[];
extern const char kAuthNegotiateDelegateWhitelist[];
extern const char kAuthSchemes[];
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index ada7737..0341101 100644
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -6559,6 +6559,11 @@
"type": "string",
"optional": true,
"description": "The name of the locale used for generating localizedName. This should be the name of one of the directories in the _locales folder of the extension, or the default_locale setting from the manifest."
+ },
+ "appInstallBubble": {
+ "type": "boolean",
+ "optional": true,
+ "description": "A flag to change the UI we show when an app is installed - a value of true means to show a bubble pointing at the new tab button (instead of the default behavior of opening the new tab page and animating the app icon)."
}
}
},
diff --git a/chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.html b/chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.html
new file mode 100644
index 0000000..61a5113c
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.html
@@ -0,0 +1,2 @@
+<script src="common.js"></script>
+<script src="app_install_bubble.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.js b/chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.js
new file mode 100644
index 0000000..d1ac1b8
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webstore_private/app_install_bubble.js
@@ -0,0 +1,25 @@
+// 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.
+
+var tests = [
+ function appInstallBubble() {
+ // See things through all the way to a successful install.
+ listenOnce(chrome.management.onInstalled, callbackPass(function(info) {
+ assertEq(info.id, appId);
+ }));
+
+ var manifest = getManifest("app/manifest.json");
+ // Begin installing.
+ chrome.webstorePrivate.beginInstallWithManifest2(
+ {'id': appId,'manifest': manifest, 'appInstallBubble':true},
+ callbackPass(function(result) {
+ assertEq(result, "");
+
+ // Now complete the installation.
+ chrome.webstorePrivate.completeInstall(appId, callbackPass());
+ }));
+ }
+];
+
+runTests(tests);
diff --git a/chrome/test/data/extensions/api_test/webstore_private/common.js b/chrome/test/data/extensions/api_test/webstore_private/common.js
index 933b032..358ced0 100644
--- a/chrome/test/data/extensions/api_test/webstore_private/common.js
+++ b/chrome/test/data/extensions/api_test/webstore_private/common.js
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// The id of the extension we're using for install tests.
+// The id of an extension we're using for install tests.
var extensionId = "enfkhcelefdadlmkffamgdlgplcionje";
+// The id of an app we're using for install tests.
+var appId = "iladmdjkfniedhfhcfoefgojhgaiaccc";
+
var assertEq = chrome.test.assertEq;
var assertNoLastError = chrome.test.assertNoLastError;
var callbackFail = chrome.test.callbackFail;