Commit 6f03db06 authored by jstritar@chromium.org's avatar jstritar@chromium.org

Fix crashes related to the extension uninstall dialog.

The uninstall prompt is owned by the settings page (the dialog's delegate), which gets destroyed when the user navigates to a new page. We need to make sure we invalidate pointers to the settings page when this occurs.

This adds an ExtensionUninstallDialog with platform specific implementations.

GTK: The settings page owns the ExtensionUninstallDialogGtk, which closes the GTK prompt when being destroyed.

VIEWS: The views framework is a bit more convoluted because views owns the prompt's views::View. We have two classes: ExtensionUninstallDialogDelegateView owned by views, and ExtensionUninstallDialogViews owned by the settings page. If the user accepts or denies the prompt, we proxy the events through ExtensionUninstallDialogViews so we can invalidate pointers back to the views widget. If the settings page is destroyed, the ExtensionUninstallDialogViews closes the views widget after invalidating pointers back to the settings page.

COCOA: You can't navigate away from the page when the prompt is open, so the dialog can't outlive its delegate.

BUG=75011
TEST=see bug.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102351 0039d316-1c4b-4281-b951-d872f2087c98
parent f2078c4f
...@@ -131,8 +131,9 @@ void ExtensionContextMenuModel::ExecuteCommand(int command_id) { ...@@ -131,8 +131,9 @@ void ExtensionContextMenuModel::ExecuteCommand(int command_id) {
} }
case UNINSTALL: { case UNINSTALL: {
AddRef(); // Balanced in Accepted() and Canceled() AddRef(); // Balanced in Accepted() and Canceled()
extension_uninstall_dialog_.reset(new ExtensionUninstallDialog(profile_)); extension_uninstall_dialog_.reset(
extension_uninstall_dialog_->ConfirmUninstall(this, extension); ExtensionUninstallDialog::Create(profile_, this));
extension_uninstall_dialog_->ConfirmUninstall(extension);
break; break;
} }
case MANAGE: { case MANAGE: {
...@@ -149,7 +150,7 @@ void ExtensionContextMenuModel::ExecuteCommand(int command_id) { ...@@ -149,7 +150,7 @@ void ExtensionContextMenuModel::ExecuteCommand(int command_id) {
} }
} }
void ExtensionContextMenuModel::ExtensionDialogAccepted() { void ExtensionContextMenuModel::ExtensionUninstallAccepted() {
if (GetExtension()) if (GetExtension())
profile_->GetExtensionService()->UninstallExtension(extension_id_, false, profile_->GetExtensionService()->UninstallExtension(extension_id_, false,
NULL); NULL);
...@@ -157,7 +158,7 @@ void ExtensionContextMenuModel::ExtensionDialogAccepted() { ...@@ -157,7 +158,7 @@ void ExtensionContextMenuModel::ExtensionDialogAccepted() {
Release(); Release();
} }
void ExtensionContextMenuModel::ExtensionDialogCanceled() { void ExtensionContextMenuModel::ExtensionUninstallCanceled() {
Release(); Release();
} }
......
...@@ -52,8 +52,8 @@ class ExtensionContextMenuModel ...@@ -52,8 +52,8 @@ class ExtensionContextMenuModel
virtual void ExecuteCommand(int command_id); virtual void ExecuteCommand(int command_id);
// ExtensionUninstallDialog::Delegate: // ExtensionUninstallDialog::Delegate:
virtual void ExtensionDialogAccepted(); virtual void ExtensionUninstallAccepted();
virtual void ExtensionDialogCanceled(); virtual void ExtensionUninstallCanceled();
private: private:
void InitCommonCommands(); void InitCommonCommands();
......
...@@ -24,7 +24,7 @@ class Profile; ...@@ -24,7 +24,7 @@ class Profile;
class InfoBarDelegate; class InfoBarDelegate;
class TabContents; class TabContents;
// Displays all the UI around extension installation and uninstallation. // Displays all the UI around extension installation.
class ExtensionInstallUI : public ImageLoadingTracker::Observer { class ExtensionInstallUI : public ImageLoadingTracker::Observer {
public: public:
enum PromptType { enum PromptType {
......
...@@ -17,21 +17,19 @@ ...@@ -17,21 +17,19 @@
// Size of extension icon in top left of dialog. // Size of extension icon in top left of dialog.
static const int kIconSize = 69; static const int kIconSize = 69;
ExtensionUninstallDialog::ExtensionUninstallDialog(Profile* profile) ExtensionUninstallDialog::ExtensionUninstallDialog(
Profile* profile,
ExtensionUninstallDialog::Delegate* delegate)
: profile_(profile), : profile_(profile),
ui_loop_(MessageLoop::current()), delegate_(delegate),
delegate_(NULL),
extension_(NULL), extension_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) { ui_loop_(MessageLoop::current()),
} ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) {}
ExtensionUninstallDialog::~ExtensionUninstallDialog() { ExtensionUninstallDialog::~ExtensionUninstallDialog() {}
}
void ExtensionUninstallDialog::ConfirmUninstall(Delegate* delegate, void ExtensionUninstallDialog::ConfirmUninstall(const Extension* extension) {
const Extension* extension) {
DCHECK(ui_loop_ == MessageLoop::current()); DCHECK(ui_loop_ == MessageLoop::current());
delegate_ = delegate;
extension_ = extension; extension_ = extension;
ExtensionResource image = ExtensionResource image =
...@@ -64,5 +62,5 @@ void ExtensionUninstallDialog::OnImageLoaded(SkBitmap* image, ...@@ -64,5 +62,5 @@ void ExtensionUninstallDialog::OnImageLoaded(SkBitmap* image,
int index) { int index) {
SetIcon(image); SetIcon(image);
Show(profile_, delegate_, extension_, &icon_); Show();
} }
...@@ -19,34 +19,45 @@ class ExtensionUninstallDialog : public ImageLoadingTracker::Observer { ...@@ -19,34 +19,45 @@ class ExtensionUninstallDialog : public ImageLoadingTracker::Observer {
class Delegate { class Delegate {
public: public:
// We call this method to signal that the uninstallation should continue. // We call this method to signal that the uninstallation should continue.
virtual void ExtensionDialogAccepted() = 0; virtual void ExtensionUninstallAccepted() = 0;
// We call this method to signal that the uninstallation should stop. // We call this method to signal that the uninstallation should stop.
virtual void ExtensionDialogCanceled() = 0; virtual void ExtensionUninstallCanceled() = 0;
protected: protected:
virtual ~Delegate() {} virtual ~Delegate() {}
}; };
explicit ExtensionUninstallDialog(Profile* profile); // Creates a platform specific implementation of ExtensionUninstallDialog.
static ExtensionUninstallDialog* Create(
Profile* profile, Delegate* delegate);
virtual ~ExtensionUninstallDialog(); virtual ~ExtensionUninstallDialog();
// This is called by the extensions management page to verify whether the // This is called to verify whether the uninstallation should proceed.
// uninstallation should proceed.
// Starts the process of showing a confirmation UI, which is split into two. // Starts the process of showing a confirmation UI, which is split into two.
// 1) Set off a 'load icon' task. // 1) Set off a 'load icon' task.
// 2) Handle the load icon response and show the UI (OnImageLoaded). // 2) Handle the load icon response and show the UI (OnImageLoaded).
void ConfirmUninstall(Delegate* delegate, const Extension* extension); void ConfirmUninstall(const Extension* extension);
protected:
// Constructor used by the derived classes.
explicit ExtensionUninstallDialog(Profile* profile, Delegate* delegate);
Profile* profile_;
// The delegate we will call Accepted/Canceled on after confirmation dialog.
Delegate* delegate_;
// The extension we are showing the dialog for.
const Extension* extension_;
// The extensions icon.
SkBitmap icon_;
private: private:
// Creates an appropriate ExtensionUninstallDialog for the platform. // Sets the icon that will be used in the dialog. If |icon| is NULL, or
static void Show(Profile* profile, // contains an empty bitmap, then we use a default icon instead.
Delegate* delegate,
const Extension* extension,
SkBitmap* icon);
// Sets the icon that will be used in any UI. If |icon| is NULL, or contains
// an empty bitmap, then a default icon will be used instead.
void SetIcon(SkBitmap* icon); void SetIcon(SkBitmap* icon);
// ImageLoadingTracker::Observer: // ImageLoadingTracker::Observer:
...@@ -54,22 +65,16 @@ class ExtensionUninstallDialog : public ImageLoadingTracker::Observer { ...@@ -54,22 +65,16 @@ class ExtensionUninstallDialog : public ImageLoadingTracker::Observer {
const ExtensionResource& resource, const ExtensionResource& resource,
int index) OVERRIDE; int index) OVERRIDE;
Profile* profile_; // Displays the prompt. This should only be called after loading the icon.
MessageLoop* ui_loop_; // The implementations of this method are platform-specific.
virtual void Show() = 0;
// The delegate we will call Accepted/Canceled on after confirmation UI.
Delegate* delegate_;
// The extension we are showing the UI for. MessageLoop* ui_loop_;
const Extension* extension_;
// Keeps track of extension images being loaded on the File thread for the // Keeps track of extension images being loaded on the File thread for the
// purpose of showing the install UI. // purpose of showing the dialog.
ImageLoadingTracker tracker_; ImageLoadingTracker tracker_;
// The extensions icon.
SkBitmap icon_;
DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialog); DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialog);
}; };
......
...@@ -282,7 +282,7 @@ void ExtensionsDOMHandler::RegisterForNotifications() { ...@@ -282,7 +282,7 @@ void ExtensionsDOMHandler::RegisterForNotifications() {
ExtensionUninstallDialog* ExtensionsDOMHandler::GetExtensionUninstallDialog() { ExtensionUninstallDialog* ExtensionsDOMHandler::GetExtensionUninstallDialog() {
if (!extension_uninstall_dialog_.get()) { if (!extension_uninstall_dialog_.get()) {
extension_uninstall_dialog_.reset( extension_uninstall_dialog_.reset(
new ExtensionUninstallDialog(Profile::FromWebUI(web_ui_))); ExtensionUninstallDialog::Create(Profile::FromWebUI(web_ui_), this));
} }
return extension_uninstall_dialog_.get(); return extension_uninstall_dialog_.get();
} }
...@@ -413,10 +413,10 @@ void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { ...@@ -413,10 +413,10 @@ void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) {
extension_id_prompting_ = extension_id; extension_id_prompting_ = extension_id;
GetExtensionUninstallDialog()->ConfirmUninstall(this, extension); GetExtensionUninstallDialog()->ConfirmUninstall(extension);
} }
void ExtensionsDOMHandler::ExtensionDialogAccepted() { void ExtensionsDOMHandler::ExtensionUninstallAccepted() {
DCHECK(!extension_id_prompting_.empty()); DCHECK(!extension_id_prompting_.empty());
bool was_terminated = false; bool was_terminated = false;
...@@ -443,7 +443,7 @@ void ExtensionsDOMHandler::ExtensionDialogAccepted() { ...@@ -443,7 +443,7 @@ void ExtensionsDOMHandler::ExtensionDialogAccepted() {
HandleRequestExtensionsData(NULL); HandleRequestExtensionsData(NULL);
} }
void ExtensionsDOMHandler::ExtensionDialogCanceled() { void ExtensionsDOMHandler::ExtensionUninstallCanceled() {
extension_id_prompting_ = ""; extension_id_prompting_ = "";
} }
......
...@@ -74,8 +74,8 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, ...@@ -74,8 +74,8 @@ class ExtensionsDOMHandler : public WebUIMessageHandler,
virtual void OnPackFailure(const std::string& error) OVERRIDE; virtual void OnPackFailure(const std::string& error) OVERRIDE;
// ExtensionUninstallDialog::Delegate: // ExtensionUninstallDialog::Delegate:
virtual void ExtensionDialogAccepted() OVERRIDE; virtual void ExtensionUninstallAccepted() OVERRIDE;
virtual void ExtensionDialogCanceled() OVERRIDE; virtual void ExtensionUninstallCanceled() OVERRIDE;
private: private:
// Callback for "requestExtensionsData" message. // Callback for "requestExtensionsData" message.
......
...@@ -40,18 +40,19 @@ class AsyncUninstaller : public ExtensionUninstallDialog::Delegate { ...@@ -40,18 +40,19 @@ class AsyncUninstaller : public ExtensionUninstallDialog::Delegate {
AsyncUninstaller(const Extension* extension, Profile* profile) AsyncUninstaller(const Extension* extension, Profile* profile)
: extension_(extension), : extension_(extension),
profile_(profile) { profile_(profile) {
extension_uninstall_dialog_.reset(new ExtensionUninstallDialog(profile)); extension_uninstall_dialog_.reset(
extension_uninstall_dialog_->ConfirmUninstall(this, extension_); ExtensionUninstallDialog::Create(profile, this));
extension_uninstall_dialog_->ConfirmUninstall(extension_);
} }
~AsyncUninstaller() {} ~AsyncUninstaller() {}
// ExtensionUninstallDialog::Delegate: // ExtensionUninstallDialog::Delegate:
virtual void ExtensionDialogAccepted() { virtual void ExtensionUninstallAccepted() {
profile_->GetExtensionService()-> profile_->GetExtensionService()->
UninstallExtension(extension_->id(), false, NULL); UninstallExtension(extension_->id(), false, NULL);
} }
virtual void ExtensionDialogCanceled() {} virtual void ExtensionUninstallCanceled() {}
private: private:
// The extension that we're loading the icon for. Weak. // The extension that we're loading the icon for. Weak.
......
...@@ -17,12 +17,28 @@ ...@@ -17,12 +17,28 @@
#include "ui/base/l10n/l10n_util_mac.h" #include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
// static namespace {
void ExtensionUninstallDialog::Show(
Profile* profile, // The Cocoa implementation of ExtensionUninstallDialog. This has a less
ExtensionUninstallDialog::Delegate* delegate, // complex life cycle than the Views and GTK implementations because the
const Extension* extension, // dialog blocks the page from navigating away and destroying the dialog,
SkBitmap* icon) { // so there's no way for the dialog to outlive its delegate.
class ExtensionUninstallDialogCocoa : public ExtensionUninstallDialog {
public:
ExtensionUninstallDialogCocoa(Profile* profile, Delegate* delegate);
virtual ~ExtensionUninstallDialogCocoa() OVERRIDE;
private:
virtual void Show() OVERRIDE;
};
ExtensionUninstallDialogCocoa::ExtensionUninstallDialogCocoa(
Profile* profile, ExtensionUninstallDialog::Delegate* delegate)
: ExtensionUninstallDialog(profile, delegate) {}
ExtensionUninstallDialogCocoa::~ExtensionUninstallDialogCocoa() {}
void ExtensionUninstallDialogCocoa::Show() {
NSAlert* alert = [[[NSAlert alloc] init] autorelease]; NSAlert* alert = [[[NSAlert alloc] init] autorelease];
NSButton* continueButton = [alert addButtonWithTitle:l10n_util::GetNSString( NSButton* continueButton = [alert addButtonWithTitle:l10n_util::GetNSString(
...@@ -37,12 +53,20 @@ void ExtensionUninstallDialog::Show( ...@@ -37,12 +53,20 @@ void ExtensionUninstallDialog::Show(
[alert setMessageText:l10n_util::GetNSStringF( [alert setMessageText:l10n_util::GetNSStringF(
IDS_EXTENSION_UNINSTALL_PROMPT_HEADING, IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
UTF8ToUTF16(extension->name()))]; UTF8ToUTF16(extension_->name()))];
[alert setAlertStyle:NSWarningAlertStyle]; [alert setAlertStyle:NSWarningAlertStyle];
[alert setIcon:gfx::SkBitmapToNSImage(*icon)]; [alert setIcon:gfx::SkBitmapToNSImage(icon_)];
if ([alert runModal] == NSAlertFirstButtonReturn) if ([alert runModal] == NSAlertFirstButtonReturn)
delegate->ExtensionDialogAccepted(); delegate_->ExtensionUninstallAccepted();
else else
delegate->ExtensionDialogCanceled(); delegate_->ExtensionUninstallCanceled();
}
} // namespace
// static
ExtensionUninstallDialog* ExtensionUninstallDialog::Create(
Profile* profile, Delegate* delegate) {
return new ExtensionUninstallDialogCocoa(profile, delegate);
} }
...@@ -26,41 +26,59 @@ namespace { ...@@ -26,41 +26,59 @@ namespace {
// Left or right margin. // Left or right margin.
const int kPanelHorizMargin = 13; const int kPanelHorizMargin = 13;
void OnResponse(GtkWidget* dialog, int response_id, // GTK implementation of the uninstall dialog.
ExtensionUninstallDialog::Delegate* delegate) { class ExtensionUninstallDialogGtk : public ExtensionUninstallDialog {
if (response_id == GTK_RESPONSE_ACCEPT) public:
delegate->ExtensionDialogAccepted(); ExtensionUninstallDialogGtk(Profile* profile, Delegate* delegate);
else virtual ~ExtensionUninstallDialogGtk() OVERRIDE;
delegate->ExtensionDialogCanceled();
private:
gtk_widget_destroy(dialog); virtual void Show() OVERRIDE;
}
CHROMEGTK_CALLBACK_1(ExtensionUninstallDialogGtk, void, OnResponse, int);
GtkWidget* dialog_;
};
ExtensionUninstallDialogGtk::ExtensionUninstallDialogGtk(
Profile* profile, ExtensionUninstallDialog::Delegate* delegate)
: ExtensionUninstallDialog(profile, delegate),
dialog_(NULL) {}
void ExtensionUninstallDialogGtk::Show() {
Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
if (!browser) {
delegate_->ExtensionUninstallCanceled();
return;
}
BrowserWindow* browser_window = browser->window();
if (!browser_window) {
delegate_->ExtensionUninstallCanceled();
return;
}
void ShowUninstallDialogGtk(GtkWindow* parent,
SkBitmap* skia_icon,
const Extension* extension,
ExtensionUninstallDialog::Delegate *delegate) {
// Build the dialog. // Build the dialog.
GtkWidget* dialog = gtk_dialog_new_with_buttons( dialog_ = gtk_dialog_new_with_buttons(
l10n_util::GetStringUTF8(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE).c_str(), l10n_util::GetStringUTF8(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE).c_str(),
parent, browser_window->GetNativeHandle(),
GTK_DIALOG_MODAL, GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_STOCK_CANCEL,
GTK_RESPONSE_CLOSE, GTK_RESPONSE_CLOSE,
l10n_util::GetStringUTF8(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON).c_str(), l10n_util::GetStringUTF8(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON).c_str(),
GTK_RESPONSE_ACCEPT, GTK_RESPONSE_ACCEPT,
NULL); NULL);
gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); gtk_dialog_set_has_separator(GTK_DIALOG(dialog_), FALSE);
// Create a two column layout. // Create a two column layout.
GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog_));
gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing); gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
GtkWidget* icon_hbox = gtk_hbox_new(FALSE, ui::kContentAreaSpacing); GtkWidget* icon_hbox = gtk_hbox_new(FALSE, ui::kContentAreaSpacing);
gtk_box_pack_start(GTK_BOX(content_area), icon_hbox, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(content_area), icon_hbox, TRUE, TRUE, 0);
// Put Icon in the left column. // Put Icon in the left column.
GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(skia_icon); GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon_);
GtkWidget* icon = gtk_image_new_from_pixbuf(pixbuf); GtkWidget* icon = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(pixbuf); g_object_unref(pixbuf);
gtk_box_pack_start(GTK_BOX(icon_hbox), icon, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(icon_hbox), icon, TRUE, TRUE, 0);
...@@ -70,36 +88,44 @@ void ShowUninstallDialogGtk(GtkWindow* parent, ...@@ -70,36 +88,44 @@ void ShowUninstallDialogGtk(GtkWindow* parent,
gtk_box_pack_start(GTK_BOX(icon_hbox), right_column_area, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(icon_hbox), right_column_area, TRUE, TRUE, 0);
std::string heading_text = l10n_util::GetStringFUTF8( std::string heading_text = l10n_util::GetStringFUTF8(
IDS_EXTENSION_UNINSTALL_PROMPT_HEADING, UTF8ToUTF16(extension->name())); IDS_EXTENSION_UNINSTALL_PROMPT_HEADING, UTF8ToUTF16(extension_->name()));
GtkWidget* heading_label = gtk_label_new(heading_text.c_str()); GtkWidget* heading_label = gtk_label_new(heading_text.c_str());
gtk_misc_set_alignment(GTK_MISC(heading_label), 0.0, 0.5); gtk_misc_set_alignment(GTK_MISC(heading_label), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(right_column_area), heading_label, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(right_column_area), heading_label, TRUE, TRUE, 0);
g_signal_connect(dialog, "response", G_CALLBACK(OnResponse), delegate); g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE);
gtk_widget_show_all(dialog); gtk_widget_show_all(dialog_);
} }
} // namespace ExtensionUninstallDialogGtk::~ExtensionUninstallDialogGtk() {
delegate_ = NULL;
// static if (dialog_) {
void ExtensionUninstallDialog::Show( gtk_widget_destroy(dialog_);
Profile* profile, dialog_ = NULL;
ExtensionUninstallDialog::Delegate* delegate,
const Extension* extension,
SkBitmap* icon) {
Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
if (!browser) {
delegate->ExtensionDialogCanceled();
return;
} }
}
BrowserWindow* browser_window = browser->window(); void ExtensionUninstallDialogGtk::OnResponse(
if (!browser_window) { GtkWidget* dialog, int response_id) {
delegate->ExtensionDialogCanceled(); CHECK_EQ(dialog_, dialog);
return;
gtk_widget_destroy(dialog_);
dialog_ = NULL;
if (delegate_) {
if (response_id == GTK_RESPONSE_ACCEPT)
delegate_->ExtensionUninstallAccepted();
else
delegate_->ExtensionUninstallCanceled();
} }
}
ShowUninstallDialogGtk(browser_window->GetNativeHandle(), } // namespace
icon, extension, delegate);
// static
// Platform specific implementation of the uninstall dialog show method.
ExtensionUninstallDialog* ExtensionUninstallDialog::Create(
Profile* profile, Delegate* delegate) {
return new ExtensionUninstallDialogGtk(profile, delegate);
} }
...@@ -91,9 +91,9 @@ void PanelSettingsMenuModel::ExecuteCommand(int command_id) { ...@@ -91,9 +91,9 @@ void PanelSettingsMenuModel::ExecuteCommand(int command_id) {
case COMMAND_UNINSTALL: case COMMAND_UNINSTALL:
// TODO(jianli): Need to handle the case that the extension API requests // TODO(jianli): Need to handle the case that the extension API requests
// the panel to be closed when the uninstall dialog is still showing. // the panel to be closed when the uninstall dialog is still showing.
extension_uninstall_dialog_.reset(new ExtensionUninstallDialog( extension_uninstall_dialog_.reset(
browser->GetProfile())); ExtensionUninstallDialog::Create(browser->GetProfile(), this));
extension_uninstall_dialog_->ConfirmUninstall(this, extension); extension_uninstall_dialog_->ConfirmUninstall(extension);
break; break;
case COMMAND_MANAGE: case COMMAND_MANAGE:
browser->ShowOptionsTab(chrome::kExtensionsSubPage); browser->ShowOptionsTab(chrome::kExtensionsSubPage);
...@@ -104,7 +104,7 @@ void PanelSettingsMenuModel::ExecuteCommand(int command_id) { ...@@ -104,7 +104,7 @@ void PanelSettingsMenuModel::ExecuteCommand(int command_id) {
} }
} }
void PanelSettingsMenuModel::ExtensionDialogAccepted() { void PanelSettingsMenuModel::ExtensionUninstallAccepted() {
const Extension* extension = panel_->GetExtension(); const Extension* extension = panel_->GetExtension();
DCHECK(extension); DCHECK(extension);
...@@ -112,5 +112,5 @@ void PanelSettingsMenuModel::ExtensionDialogAccepted() { ...@@ -112,5 +112,5 @@ void PanelSettingsMenuModel::ExtensionDialogAccepted() {
UninstallExtension(extension->id(), false, NULL); UninstallExtension(extension->id(), false, NULL);
} }
void PanelSettingsMenuModel::ExtensionDialogCanceled() { void PanelSettingsMenuModel::ExtensionUninstallCanceled() {
} }
...@@ -28,8 +28,8 @@ class PanelSettingsMenuModel : public ui::SimpleMenuModel, ...@@ -28,8 +28,8 @@ class PanelSettingsMenuModel : public ui::SimpleMenuModel,
virtual void ExecuteCommand(int command_id) OVERRIDE; virtual void ExecuteCommand(int command_id) OVERRIDE;
// ExtensionUninstallDialog::Delegate: // ExtensionUninstallDialog::Delegate:
virtual void ExtensionDialogAccepted() OVERRIDE; virtual void ExtensionUninstallAccepted() OVERRIDE;
virtual void ExtensionDialogCanceled() OVERRIDE; virtual void ExtensionUninstallCanceled() OVERRIDE;
private: private:
friend class PanelBrowserTest; friend class PanelBrowserTest;
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
#include "grit/generated_resources.h" #include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/compositor/compositor.h"
#include "ui/gfx/compositor/layer.h"
#include "views/controls/image_view.h" #include "views/controls/image_view.h"
#include "views/controls/label.h" #include "views/controls/label.h"
#include "views/layout/layout_constants.h" #include "views/layout/layout_constants.h"
...@@ -26,135 +28,211 @@ namespace { ...@@ -26,135 +28,211 @@ namespace {
const int kRightColumnWidth = 210; const int kRightColumnWidth = 210;
const int kIconSize = 69; const int kIconSize = 69;
class ExtensionUninstallDialogView : public views::DialogDelegateView { class ExtensionUninstallDialogDelegateView;
// Views implementation of the uninstall dialog.
class ExtensionUninstallDialogViews : public ExtensionUninstallDialog {
public: public:
ExtensionUninstallDialogView(ExtensionUninstallDialog::Delegate* delegate, ExtensionUninstallDialogViews(Profile* profile,
const Extension* extension, ExtensionUninstallDialog::Delegate* delegate);
SkBitmap* icon) virtual ~ExtensionUninstallDialogViews();
: delegate_(delegate),
icon_(NULL) { // Forwards the accept and cancels to the delegate.
// Scale down to icon size, but allow smaller icons (don't scale up). void ExtensionUninstallAccepted();
gfx::Size size(icon->width(), icon->height()); void ExtensionUninstallCanceled();
if (size.width() > kIconSize || size.height() > kIconSize)
size = gfx::Size(kIconSize, kIconSize); ExtensionUninstallDialogDelegateView* view() { return view_; }
icon_ = new views::ImageView();
icon_->SetImageSize(size);
icon_->SetImage(*icon);
AddChildView(icon_);
heading_ = new views::Label(UTF16ToWide(
l10n_util::GetStringFUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
UTF8ToUTF16(extension->name()))));
heading_->SetMultiLine(true);
heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
AddChildView(heading_);
}
private: private:
// views::DialogDelegateView: void Show() OVERRIDE;
ExtensionUninstallDialogDelegateView* view_;
DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews);
};
// The dialog's view, owned by the views framework.
class ExtensionUninstallDialogDelegateView : public views::DialogDelegateView {
public:
ExtensionUninstallDialogDelegateView(
ExtensionUninstallDialogViews* dialog_view,
const Extension* extension,
SkBitmap* icon);
virtual ~ExtensionUninstallDialogDelegateView();
// Called when the ExtensionUninstallDialog has been destroyed to make sure
// we invalidate pointers.
void DialogDestroyed() { dialog_ = NULL; }
private:
// views::DialogDelegate:
virtual std::wstring GetDialogButtonLabel( virtual std::wstring GetDialogButtonLabel(
MessageBoxFlags::DialogButton button) const OVERRIDE { MessageBoxFlags::DialogButton button) const OVERRIDE;
switch (button) {
case MessageBoxFlags::DIALOGBUTTON_OK:
return UTF16ToWide(
l10n_util::GetStringUTF16(
IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON));
case MessageBoxFlags::DIALOGBUTTON_CANCEL:
return UTF16ToWide(l10n_util::GetStringUTF16(IDS_CANCEL));
default:
NOTREACHED();
return L"";
}
}
virtual int GetDefaultDialogButton() const OVERRIDE { virtual int GetDefaultDialogButton() const OVERRIDE {
return MessageBoxFlags::DIALOGBUTTON_CANCEL; return MessageBoxFlags::DIALOGBUTTON_CANCEL;
} }
virtual bool Accept() OVERRIDE { virtual bool Accept() OVERRIDE;
delegate_->ExtensionDialogAccepted(); virtual bool Cancel() OVERRIDE;
return true;
}
virtual bool Cancel() OVERRIDE {
delegate_->ExtensionDialogCanceled();
return true;
}
// views::WidgetDelegate: // views::WidgetDelegate:
virtual bool IsModal() const OVERRIDE { return true; } virtual bool IsModal() const OVERRIDE { return true; }
virtual std::wstring GetWindowTitle() const OVERRIDE { virtual views::View* GetContentsView() OVERRIDE { return this; }
return UTF16ToWide( virtual std::wstring GetWindowTitle() const OVERRIDE;
l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE));
}
virtual views::View* GetContentsView() { return this; }
// views::View: // views::View:
virtual gfx::Size GetPreferredSize() OVERRIDE { virtual gfx::Size GetPreferredSize() OVERRIDE;
int width = kRightColumnWidth;
width += kIconSize;
width += views::kPanelHorizMargin * 3;
int height = views::kPanelVertMargin * 2; virtual void Layout() OVERRIDE;
height += heading_->GetHeightForWidth(kRightColumnWidth);
return gfx::Size(width, ExtensionUninstallDialogViews* dialog_;
std::max(height, kIconSize + views::kPanelVertMargin * 2));
}
virtual void Layout() OVERRIDE {
int x = views::kPanelHorizMargin;
int y = views::kPanelVertMargin;
heading_->SizeToFit(kRightColumnWidth);
if (heading_->height() <= kIconSize) {
icon_->SetBounds(x, y, kIconSize, kIconSize);
x += kIconSize;
x += views::kPanelHorizMargin;
heading_->SetX(x);
heading_->SetY(y + (kIconSize - heading_->height()) / 2);
} else {
icon_->SetBounds(x,
y + (heading_->height() - kIconSize) / 2,
kIconSize,
kIconSize);
x += kIconSize;
x += views::kPanelHorizMargin;
heading_->SetX(x);
heading_->SetY(y);
}
}
ExtensionUninstallDialog::Delegate* delegate_;
views::ImageView* icon_; views::ImageView* icon_;
views::Label* heading_; views::Label* heading_;
DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogView); DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView);
}; };
} // namespace ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
Profile* profile, ExtensionUninstallDialog::Delegate* delegate)
: ExtensionUninstallDialog(profile, delegate) {}
// static ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
void ExtensionUninstallDialog::Show( // Close the widget (the views framework will delete view_).
Profile* profile, if (view_) {
ExtensionUninstallDialog::Delegate* delegate, view_->DialogDestroyed();
const Extension* extension, view_->GetWidget()->CloseNow();
SkBitmap* icon) { }
Browser* browser = BrowserList::GetLastActiveWithProfile(profile); }
void ExtensionUninstallDialogViews::Show() {
Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
if (!browser) { if (!browser) {
delegate->ExtensionDialogCanceled(); delegate_->ExtensionUninstallCanceled();
return; return;
} }
BrowserWindow* window = browser->window(); BrowserWindow* window = browser->window();
if (!window) { if (!window) {
delegate->ExtensionDialogCanceled(); delegate_->ExtensionUninstallCanceled();
return; return;
} }
browser::CreateViewsWindow(window->GetNativeHandle(), view_ = new ExtensionUninstallDialogDelegateView(this, extension_, &icon_);
new ExtensionUninstallDialogView(delegate, extension, icon))->Show(); browser::CreateViewsWindow(window->GetNativeHandle(), view_)->Show();
}
void ExtensionUninstallDialogViews::ExtensionUninstallAccepted() {
// The widget gets destroyed when the dialog is accepted.
view_ = NULL;
delegate_->ExtensionUninstallAccepted();
}
void ExtensionUninstallDialogViews::ExtensionUninstallCanceled() {
// The widget gets destroyed when the dialog is canceled.
view_ = NULL;
delegate_->ExtensionUninstallCanceled();
}
ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
ExtensionUninstallDialogViews* dialog_view,
const Extension* extension,
SkBitmap* icon)
: dialog_(dialog_view) {
// Scale down to icon size, but allow smaller icons (don't scale up).
gfx::Size size(icon->width(), icon->height());
if (size.width() > kIconSize || size.height() > kIconSize)
size = gfx::Size(kIconSize, kIconSize);
icon_ = new views::ImageView();
icon_->SetImageSize(size);
icon_->SetImage(*icon);
AddChildView(icon_);
heading_ = new views::Label(UTF16ToWide(
l10n_util::GetStringFUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
UTF8ToUTF16(extension->name()))));
heading_->SetMultiLine(true);
AddChildView(heading_);
}
ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() {
}
std::wstring ExtensionUninstallDialogDelegateView::GetDialogButtonLabel(
MessageBoxFlags::DialogButton button) const {
switch (button) {
case MessageBoxFlags::DIALOGBUTTON_OK:
return UTF16ToWide(
l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON));
case MessageBoxFlags::DIALOGBUTTON_CANCEL:
return UTF16ToWide(l10n_util::GetStringUTF16(IDS_CANCEL));
default:
NOTREACHED();
return L"";
}
}
bool ExtensionUninstallDialogDelegateView::Accept() {
if (dialog_)
dialog_->ExtensionUninstallAccepted();
return true;
}
bool ExtensionUninstallDialogDelegateView::Cancel() {
if (dialog_)
dialog_->ExtensionUninstallCanceled();
return true;
}
std::wstring ExtensionUninstallDialogDelegateView::GetWindowTitle() const {
return UTF16ToWide(
l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE));
}
gfx::Size ExtensionUninstallDialogDelegateView::GetPreferredSize() {
int width = kRightColumnWidth;
width += kIconSize;
width += views::kPanelHorizMargin * 3;
int height = views::kPanelVertMargin * 2;
height += heading_->GetHeightForWidth(kRightColumnWidth);
return gfx::Size(width,
std::max(height, kIconSize + views::kPanelVertMargin * 2));
}
void ExtensionUninstallDialogDelegateView::Layout() {
int x = views::kPanelHorizMargin;
int y = views::kPanelVertMargin;
heading_->SizeToFit(kRightColumnWidth);
if (heading_->height() <= kIconSize) {
icon_->SetBounds(x, y, kIconSize, kIconSize);
x += kIconSize;
x += views::kPanelHorizMargin;
heading_->SetX(x);
heading_->SetY(y + (kIconSize - heading_->height()) / 2);
} else {
icon_->SetBounds(x,
y + (heading_->height() - kIconSize) / 2,
kIconSize,
kIconSize);
x += kIconSize;
x += views::kPanelHorizMargin;
heading_->SetX(x);
heading_->SetY(y);
}
}
} // namespace
// static
ExtensionUninstallDialog* ExtensionUninstallDialog::Create(
Profile* profile, Delegate* delegate) {
return new ExtensionUninstallDialogViews(profile, delegate);
} }
...@@ -642,9 +642,9 @@ void AppLauncherHandler::HandleUninstallApp(const ListValue* args) { ...@@ -642,9 +642,9 @@ void AppLauncherHandler::HandleUninstallApp(const ListValue* args) {
scoped_ptr<AutoReset<bool> > auto_reset; scoped_ptr<AutoReset<bool> > auto_reset;
if (NewTabUI::NTP4Enabled()) if (NewTabUI::NTP4Enabled())
auto_reset.reset(new AutoReset<bool>(&ignore_changes_, true)); auto_reset.reset(new AutoReset<bool>(&ignore_changes_, true));
ExtensionDialogAccepted(); ExtensionUninstallAccepted();
} else { } else {
GetExtensionUninstallDialog()->ConfirmUninstall(this, extension); GetExtensionUninstallDialog()->ConfirmUninstall(extension);
} }
} }
...@@ -913,7 +913,7 @@ void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) { ...@@ -913,7 +913,7 @@ void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) {
GetExtensionInstallUI()->ConfirmReEnable(this, extension); GetExtensionInstallUI()->ConfirmReEnable(this, extension);
} }
void AppLauncherHandler::ExtensionDialogAccepted() { void AppLauncherHandler::ExtensionUninstallAccepted() {
// Do the uninstall work here. // Do the uninstall work here.
DCHECK(!extension_id_prompting_.empty()); DCHECK(!extension_id_prompting_.empty());
...@@ -930,7 +930,7 @@ void AppLauncherHandler::ExtensionDialogAccepted() { ...@@ -930,7 +930,7 @@ void AppLauncherHandler::ExtensionDialogAccepted() {
extension_id_prompting_ = ""; extension_id_prompting_ = "";
} }
void AppLauncherHandler::ExtensionDialogCanceled() { void AppLauncherHandler::ExtensionUninstallCanceled() {
extension_id_prompting_ = ""; extension_id_prompting_ = "";
} }
...@@ -958,7 +958,7 @@ void AppLauncherHandler::InstallUIProceed() { ...@@ -958,7 +958,7 @@ void AppLauncherHandler::InstallUIProceed() {
} }
void AppLauncherHandler::InstallUIAbort(bool user_initiated) { void AppLauncherHandler::InstallUIAbort(bool user_initiated) {
// We record the histograms here because ExtensionDialogCanceled is also // We record the histograms here because ExtensionUninstallCanceled is also
// called when the extension uninstall dialog is canceled. // called when the extension uninstall dialog is canceled.
const Extension* extension = const Extension* extension =
extension_service_->GetExtensionById(extension_id_prompting_, true); extension_service_->GetExtensionById(extension_id_prompting_, true);
...@@ -968,13 +968,13 @@ void AppLauncherHandler::InstallUIAbort(bool user_initiated) { ...@@ -968,13 +968,13 @@ void AppLauncherHandler::InstallUIAbort(bool user_initiated) {
ExtensionService::RecordPermissionMessagesHistogram( ExtensionService::RecordPermissionMessagesHistogram(
extension, histogram_name.c_str()); extension, histogram_name.c_str());
ExtensionDialogCanceled(); ExtensionUninstallCanceled();
} }
ExtensionUninstallDialog* AppLauncherHandler::GetExtensionUninstallDialog() { ExtensionUninstallDialog* AppLauncherHandler::GetExtensionUninstallDialog() {
if (!extension_uninstall_dialog_.get()) { if (!extension_uninstall_dialog_.get()) {
extension_uninstall_dialog_.reset( extension_uninstall_dialog_.reset(
new ExtensionUninstallDialog(Profile::FromWebUI(web_ui_))); ExtensionUninstallDialog::Create(Profile::FromWebUI(web_ui_), this));
} }
return extension_uninstall_dialog_.get(); return extension_uninstall_dialog_.get();
} }
......
...@@ -141,8 +141,8 @@ class AppLauncherHandler : public WebUIMessageHandler, ...@@ -141,8 +141,8 @@ class AppLauncherHandler : public WebUIMessageHandler,
void PromptToEnableApp(const std::string& extension_id); void PromptToEnableApp(const std::string& extension_id);
// ExtensionUninstallDialog::Delegate: // ExtensionUninstallDialog::Delegate:
virtual void ExtensionDialogAccepted() OVERRIDE; virtual void ExtensionUninstallAccepted() OVERRIDE;
virtual void ExtensionDialogCanceled() OVERRIDE; virtual void ExtensionUninstallCanceled() OVERRIDE;
// ExtensionInstallUI::Delegate: // ExtensionInstallUI::Delegate:
virtual void InstallUIProceed() OVERRIDE; virtual void InstallUIProceed() OVERRIDE;
......
...@@ -210,7 +210,7 @@ ExtensionUninstallDialog* ...@@ -210,7 +210,7 @@ ExtensionUninstallDialog*
ExtensionSettingsHandler::GetExtensionUninstallDialog() { ExtensionSettingsHandler::GetExtensionUninstallDialog() {
if (!extension_uninstall_dialog_.get()) { if (!extension_uninstall_dialog_.get()) {
extension_uninstall_dialog_.reset( extension_uninstall_dialog_.reset(
new ExtensionUninstallDialog(Profile::FromWebUI(web_ui_))); ExtensionUninstallDialog::Create(Profile::FromWebUI(web_ui_), this));
} }
return extension_uninstall_dialog_.get(); return extension_uninstall_dialog_.get();
} }
...@@ -345,10 +345,10 @@ void ExtensionSettingsHandler::HandleUninstallMessage(const ListValue* args) { ...@@ -345,10 +345,10 @@ void ExtensionSettingsHandler::HandleUninstallMessage(const ListValue* args) {
extension_id_prompting_ = extension_id; extension_id_prompting_ = extension_id;
GetExtensionUninstallDialog()->ConfirmUninstall(this, extension); GetExtensionUninstallDialog()->ConfirmUninstall(extension);
} }
void ExtensionSettingsHandler::ExtensionDialogAccepted() { void ExtensionSettingsHandler::ExtensionUninstallAccepted() {
DCHECK(!extension_id_prompting_.empty()); DCHECK(!extension_id_prompting_.empty());
bool was_terminated = false; bool was_terminated = false;
...@@ -376,7 +376,7 @@ void ExtensionSettingsHandler::ExtensionDialogAccepted() { ...@@ -376,7 +376,7 @@ void ExtensionSettingsHandler::ExtensionDialogAccepted() {
HandleRequestExtensionsData(NULL); HandleRequestExtensionsData(NULL);
} }
void ExtensionSettingsHandler::ExtensionDialogCanceled() { void ExtensionSettingsHandler::ExtensionUninstallCanceled() {
extension_id_prompting_ = ""; extension_id_prompting_ = "";
} }
......
...@@ -145,8 +145,8 @@ class ExtensionSettingsHandler : public OptionsPageUIHandler, ...@@ -145,8 +145,8 @@ class ExtensionSettingsHandler : public OptionsPageUIHandler,
// ExtensionUninstallDialog::Delegate implementation, used for receiving // ExtensionUninstallDialog::Delegate implementation, used for receiving
// notification about uninstall confirmation dialog selections. // notification about uninstall confirmation dialog selections.
virtual void ExtensionDialogAccepted() OVERRIDE; virtual void ExtensionUninstallAccepted() OVERRIDE;
virtual void ExtensionDialogCanceled() OVERRIDE; virtual void ExtensionUninstallCanceled() OVERRIDE;
private: private:
// Helper that lists the current active html pages for an extension. // Helper that lists the current active html pages for an extension.
......
...@@ -87,11 +87,10 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { ...@@ -87,11 +87,10 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate {
// For Dialog boxes, this is called when the user presses the "OK" button, // For Dialog boxes, this is called when the user presses the "OK" button,
// or the Enter key. Can also be called on Esc key or close button // or the Enter key. Can also be called on Esc key or close button
// presses if there is no "Cancel" button. This function should return // presses if there is no "Cancel" button. This function should return
// true if the window can be closed after the window can be closed after // true if the window can be closed after it returns, or false if it must
// it returns, or false if it must remain open. If |window_closing| is // remain open. If |window_closing| is true, it means that this handler is
// true, it means that this handler is being called because the window is // being called because the window is being closed (e.g. by Window::Close)
// being closed (e.g. by Window::Close) and there is no Cancel handler, // and there is no Cancel handler, so Accept is being called instead.
// so Accept is being called instead.
virtual bool Accept(bool window_closing); virtual bool Accept(bool window_closing);
virtual bool Accept(); virtual bool Accept();
......
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