diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/extensions/crx_installer.cc | 71 | ||||
-rw-r--r-- | chrome/browser/extensions/crx_installer.h | 31 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_file_util.cc | 9 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_ui.cc | 35 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_ui.h | 12 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_prefs.cc | 2 | ||||
-rw-r--r-- | chrome/browser/extensions/sandboxed_extension_unpacker.cc | 7 | ||||
-rw-r--r-- | chrome/browser/extensions/theme_preview_infobar_delegate.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/theme_preview_infobar_delegate.h | 3 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_install_prompt.cc | 194 | ||||
-rw-r--r-- | chrome/browser/views/location_bar_view.cc | 10 |
11 files changed, 339 insertions, 39 deletions
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 60fe23e..5fede2e 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -13,6 +13,8 @@ #include "chrome/browser/extensions/extension_file_util.h" #include "chrome/common/extensions/extension_error_reporter.h" #include "grit/chromium_strings.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "webkit/glue/image_decoder.h" namespace { // Helper function to delete files. This is used to avoid ugly casts which @@ -116,6 +118,7 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, } if (client_.get()) { + DecodeInstallIcon(); ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &CrxInstaller::ConfirmInstall)); } else { @@ -123,16 +126,60 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, } } -void CrxInstaller::ConfirmInstall() { - if (!client_->ConfirmInstall(extension_.get())) { - // We're done. Since we don't post any more tasks to ourselves, our ref - // count should go to zero and we die. The destructor will clean up the temp - // dir. +void CrxInstaller::DecodeInstallIcon() { + std::map<int, std::string>::const_iterator iter = + extension_->icons().find(128); + if (iter == extension_->icons().end()) + return; + + FilePath path = extension_->GetResourcePath(iter->second); + std::string file_contents; + if (!file_util::ReadFileToString(path, &file_contents)) { + LOG(ERROR) << "Could not read icon file: " + << WideToUTF8(path.ToWStringHack()); + return; + } + + // Decode the image using WebKit's image decoder. + const unsigned char* data = + reinterpret_cast<const unsigned char*>(file_contents.data()); + webkit_glue::ImageDecoder decoder; + scoped_ptr<SkBitmap> decoded(new SkBitmap()); + *decoded = decoder.Decode(data, file_contents.length()); + if(decoded->empty()) { + LOG(ERROR) << "Could not decode icon file: " + << WideToUTF8(path.ToWStringHack()); return; } + if (decoded->width() != 128 || decoded->height() != 128) { + LOG(ERROR) << "Icon file has unexpected size: " + << IntToString(decoded->width()) << "x" + << IntToString(decoded->height()); + return; + } + + install_icon_.reset(decoded.release()); +} + +void CrxInstaller::ConfirmInstall() { + AddRef(); // balanced in ContinueInstall() and AbortInstall(). + + client_->ConfirmInstall(this, extension_.get(), install_icon_.get()); +} + +void CrxInstaller::ContinueInstall() { file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &CrxInstaller::CompleteInstall)); + + Release(); // balanced in ConfirmInstall(). +} + +void CrxInstaller::AbortInstall() { + Release(); // balanced in ConfirmInstall(). + + // We're done. Since we don't post any more tasks to ourself, our ref count + // should go to zero and we die. The destructor will clean up the temp dir. } void CrxInstaller::CompleteInstall() { @@ -156,15 +203,23 @@ void CrxInstaller::CompleteInstall() { return; } - extension_->set_path(version_dir); - extension_->set_location(install_source_); - if (install_type == Extension::REINSTALL) { // We use this as a signal to switch themes. ReportOverinstallFromFileThread(); return; } + // This is lame, but we must reload the extension because absolute paths + // inside the content scripts are established inside InitFromValue() and we + // just moved the extension. + // TODO(aa): All paths to resources inside extensions should be created + // lazily and based on the Extension's root path at that moment. + std::string error; + extension_.reset(extension_file_util::LoadExtension(version_dir, true, + &error)); + DCHECK(error.empty()); + extension_->set_location(install_source_); + ReportSuccessFromFileThread(); } diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index 5c373d9..0ea8398 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -14,6 +14,9 @@ #include "chrome/browser/extensions/sandboxed_extension_unpacker.h" #include "chrome/common/extensions/extension.h" +class CrxInstaller; +class SkBitmap; + // Classes that want to know about install completion, or that want to have an // opportunity to reject the unpacked extension before installation, should // implement this interface. @@ -22,8 +25,17 @@ class CrxInstallerClient public: virtual ~CrxInstallerClient() {} - // Return true to indicate that installation should proceed, false otherwise. - virtual bool ConfirmInstall(Extension* extension) = 0; + // This is called by the installer to verify whether the installation should + // proceed. Clients can use this time to put up UI, or to do any checks they + // need to on the unpacked extension. + // + // Clients *MUST* eventually call either ContinueInstall() or AbortInstall() + // on |installer|. + // + // Clients should not AddRef() |installer|. + virtual void ConfirmInstall(CrxInstaller* installer, + Extension* extension, + SkBitmap* icon) = 0; // Installation was successful. virtual void OnInstallSuccess(Extension* extension) = 0; @@ -79,6 +91,14 @@ class CrxInstaller : public SandboxedExtensionUnpackerClient { ExtensionsService* frontend, CrxInstallerClient* client); + // Clients should call this method after ConfirmInstall() if they want the + // installation to continue. + void ContinueInstall(); + + // Clients should call this method after ConfirmInstall() if they want the + // installation to stop. + void AbortInstall(); + private: CrxInstaller(const FilePath& crx_path, const FilePath& install_directory, @@ -96,6 +116,10 @@ class CrxInstaller : public SandboxedExtensionUnpackerClient { const FilePath& extension_dir, Extension* extension); + // Read the icon from the extension if present and decode it into + // install_icon_. + void DecodeInstallIcon(); + // Runs on the UI thread. Confirms with the user (via CrxInstallerClient) that // it is OK to install this extension. void ConfirmInstall(); @@ -145,6 +169,9 @@ class CrxInstaller : public SandboxedExtensionUnpackerClient { // ExtensionsService on success, or delete it on failure. scoped_ptr<Extension> extension_; + // The icon we will display in the installation UI, if any. + scoped_ptr<SkBitmap> install_icon_; + // The temp directory extension resources were unpacked to. We own this and // must delete it when we are done with it. FilePath temp_dir_; diff --git a/chrome/browser/extensions/extension_file_util.cc b/chrome/browser/extensions/extension_file_util.cc index b46404c..9020b1b 100644 --- a/chrome/browser/extensions/extension_file_util.cc +++ b/chrome/browser/extensions/extension_file_util.cc @@ -254,13 +254,12 @@ Extension* LoadExtension(const FilePath& extension_path, bool require_key, for (PageActionMap::const_iterator i(page_actions.begin()); i != page_actions.end(); ++i) { PageAction* page_action = i->second; - const std::vector<FilePath>& icon_paths = page_action->icon_paths(); - for (std::vector<FilePath>::const_iterator iter = icon_paths.begin(); + const std::vector<std::string>& icon_paths = page_action->icon_paths(); + for (std::vector<std::string>::const_iterator iter = icon_paths.begin(); iter != icon_paths.end(); ++iter) { - FilePath path = *iter; - if (!file_util::PathExists(path)) { + if (!file_util::PathExists(extension->GetResourcePath(*iter))) { *error = StringPrintf("Could not load icon '%s' for page action.", - WideToUTF8(path.ToWStringHack()).c_str()); + iter->c_str()); return NULL; } } diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc index 4416701..135aac7 100644 --- a/chrome/browser/extensions/extension_install_ui.cc +++ b/chrome/browser/extensions/extension_install_ui.cc @@ -4,12 +4,17 @@ #include "chrome/browser/extensions/extension_install_ui.h" +#include <map> + #include "app/l10n_util.h" -#include "grit/chromium_strings.h" +#include "base/file_util.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/theme_preview_infobar_delegate.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/extensions/extension.h" +#include "grit/chromium_strings.h" #if defined(OS_WIN) #include "app/win_util.h" @@ -23,24 +28,24 @@ ExtensionInstallUI::ExtensionInstallUI(Profile* profile) : profile_(profile), ui_loop_(MessageLoop::current()) { } -bool ExtensionInstallUI::ConfirmInstall(Extension* extension) { +void ExtensionInstallUI::ConfirmInstall(CrxInstaller* installer, + Extension* extension, + SkBitmap* install_icon) { DCHECK(ui_loop_ == MessageLoop::current()); // We special-case themes to not show any confirm UI. Instead they are // immediately installed, and then we show an infobar (see OnInstallSuccess) // to allow the user to revert if they don't like it. - if (extension->IsTheme()) - return true; + if (extension->IsTheme()) { + installer->ContinueInstall(); + return; + } #if defined(OS_WIN) - if (win_util::MessageBox(GetForegroundWindow(), - L"Are you sure you want to install this extension?\n\n" - L"You should only install extensions from sources you trust.", - l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), - MB_OKCANCEL) != IDOK) { - return false; - } + ShowExtensionInstallPrompt(profile_, installer, extension, install_icon); + #elif defined(OS_MACOSX) + // TODO(port): Implement nicer UI. // Using CoreFoundation to do this dialog is unimaginably lame but will do // until the UI is redone. scoped_cftyperef<CFStringRef> product_name( @@ -53,13 +58,15 @@ bool ExtensionInstallUI::ConfirmInstall(Extension* extension) { "This is a temporary message and it will be removed when " "extensions UI is finalized."), NULL, CFSTR("Cancel"), NULL, &response)) { - return false; + installer->AbortInstall(); + } else { + installer->ContinueInstall(); } #else + // TODO(port): Implement some UI. NOTREACHED(); + installer->ContinueInstall(); #endif // OS_* - - return true; } void ExtensionInstallUI::OnInstallSuccess(Extension* extension) { diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h index 1fc0379..9cdc5ac 100644 --- a/chrome/browser/extensions/extension_install_ui.h +++ b/chrome/browser/extensions/extension_install_ui.h @@ -16,16 +16,20 @@ class Profile; class SandboxedExtensionUnpacker; // Displays all the UI around extension installation. -// -// TODO(aa): This will become a view and move to browser/views/extensions in the -// future. class ExtensionInstallUI : public CrxInstallerClient { public: + // NOTE: The implementation of this is platform-specific. + static void ShowExtensionInstallPrompt(Profile* profile, + CrxInstaller* installer, + Extension* extension, + SkBitmap* install_icon); + ExtensionInstallUI(Profile* profile); private: // CrxInstallerClient - virtual bool ConfirmInstall(Extension* extension); + virtual void ConfirmInstall(CrxInstaller* installer, Extension* extension, + SkBitmap* icon); virtual void OnInstallSuccess(Extension* extension); virtual void OnInstallFailure(const std::string& error); virtual void OnOverinstallAttempted(Extension* extension); diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 08a99a4..7b4d061 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -213,7 +213,7 @@ void ExtensionPrefs::OnExtensionInstalled(Extension* extension) { FilePath::StringType path = MakePathRelative(install_directory_, extension->path(), NULL); UpdateExtensionPref(id, kPrefPath, Value::CreateStringValue(path)); - prefs_->ScheduleSavePersistentPrefs(); + prefs_->SavePersistentPrefs(); } void ExtensionPrefs::OnExtensionUninstalled(const Extension* extension, diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc index 46a8382..c736f39 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc @@ -119,6 +119,13 @@ void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( // originals are gone for good. extension_.reset(new Extension); std::string manifest_error; + + // Update the path to refer to the temporary location. We do this because + // clients may want to use resources inside the extension before it is + // installed and they need the correct path. For example, the install UI shows + // one of the icons from the extension. + extension_->set_path(extension_root_); + if (!extension_->InitFromValue(*final_manifest, true, // require id &manifest_error)) { ReportFailure(std::string("Manifest is invalid: ") + diff --git a/chrome/browser/extensions/theme_preview_infobar_delegate.cc b/chrome/browser/extensions/theme_preview_infobar_delegate.cc index d22ebda..a1a179f 100644 --- a/chrome/browser/extensions/theme_preview_infobar_delegate.cc +++ b/chrome/browser/extensions/theme_preview_infobar_delegate.cc @@ -21,7 +21,7 @@ void ThemePreviewInfobarDelegate::InfoBarClosed() { } std::wstring ThemePreviewInfobarDelegate::GetMessageText() const { - return l10n_util::GetStringF(IDS_THEME_PREVIEW_INFOBAR_LABEL, + return l10n_util::GetStringF(IDS_THEME_INSTALL_INFOBAR_LABEL, UTF8ToWide(name_)); } @@ -44,7 +44,7 @@ std::wstring ThemePreviewInfobarDelegate::GetButtonLabel( ConfirmInfoBarDelegate::InfoBarButton button) const { switch (button) { case BUTTON_CANCEL: - return l10n_util::GetString(IDS_THEME_PREVIEW_INFOBAR_UNDO_BUTTON); + return l10n_util::GetString(IDS_THEME_INSTALL_INFOBAR_UNDO_BUTTON); default: return L""; } diff --git a/chrome/browser/extensions/theme_preview_infobar_delegate.h b/chrome/browser/extensions/theme_preview_infobar_delegate.h index 54a8507..dcc34b6 100644 --- a/chrome/browser/extensions/theme_preview_infobar_delegate.h +++ b/chrome/browser/extensions/theme_preview_infobar_delegate.h @@ -14,6 +14,9 @@ class TabContents; // When a user installs a theme, we display it immediately, but provide an // infobar allowing them to cancel. +// +// TODO(aa): Rename this to ThemeInstalledInfoBarDelegate, since it isn't +// used for previewing anymore. class ThemePreviewInfobarDelegate : public ConfirmInfoBarDelegate { public: ThemePreviewInfobarDelegate(TabContents* tab_contents, diff --git a/chrome/browser/views/extensions/extension_install_prompt.cc b/chrome/browser/views/extensions/extension_install_prompt.cc new file mode 100644 index 0000000..e110464 --- /dev/null +++ b/chrome/browser/views/extensions/extension_install_prompt.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2009 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 "app/l10n_util.h" +#include "base/file_util.h" +#include "base/rand_util.h" +#include "base/string_util.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/extensions/extension_install_ui.h" +#include "chrome/common/extensions/extension.h" +#include "grit/generated_resources.h" +#include "views/controls/image_view.h" +#include "views/controls/label.h" +#include "views/controls/link.h" +#include "views/standard_layout.h" +#include "views/view.h" +#include "views/window/dialog_delegate.h" +#include "views/window/window.h" + +class Profile; + +namespace { + +const int kRightColumnWidth = 290; + +// Implements the extension installation prompt for Windows. +// TODO(aa): It would be cool to add an "extensions threat level" when we have +// granular permissions implemented: +// - red: npapi +// - orange: access to any domains +// - yellow: access to browser data +// - green: nothing +// We could have a collection of funny descriptions for each color. +class InstallDialogContent : public views::View, public views::DialogDelegate { + public: + InstallDialogContent(CrxInstaller* crx_installer, Extension* extension, + SkBitmap* icon) + : crx_installer_(crx_installer), icon_(NULL) { + if (icon) { + icon_ = new views::ImageView(); + icon_->SetImage(*icon); + AddChildView(icon_); + } + + heading_ = new views::Label( + l10n_util::GetStringF(IDS_EXTENSION_PROMPT_HEADING, + UTF8ToWide(extension->name()))); + heading_->SetFont(heading_->GetFont().DeriveFont(1, gfx::Font::BOLD)); + heading_->SetMultiLine(true); + heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + AddChildView(heading_); + + // Pick a random warning. + std::wstring warnings[] = { + l10n_util::GetString(IDS_EXTENSION_PROMPT_WARNING_1), + l10n_util::GetString(IDS_EXTENSION_PROMPT_WARNING_2), + l10n_util::GetString(IDS_EXTENSION_PROMPT_WARNING_3) + }; + warning_ = new views::Label( + warnings[base::RandInt(0, arraysize(warnings) - 1)]); + warning_->SetMultiLine(true); + warning_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + AddChildView(warning_); + + severe_ = new views::Label( + l10n_util::GetString(IDS_EXTENSION_PROMPT_WARNING_SEVERE)); + severe_->SetMultiLine(true); + severe_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + severe_->SetFont(heading_->GetFont().DeriveFont(0, gfx::Font::BOLD)); + severe_->SetColor(SK_ColorRED); + AddChildView(severe_); + } + + private: + // DialogDelegate + virtual std::wstring GetDialogButtonLabel( + MessageBoxFlags::DialogButton button) const { + switch (button) { + case MessageBoxFlags::DIALOGBUTTON_OK: + return l10n_util::GetString(IDS_EXTENSION_PROMPT_INSTALL_BUTTON); + case MessageBoxFlags::DIALOGBUTTON_CANCEL: + return l10n_util::GetString(IDS_EXTENSION_PROMPT_CANCEL_BUTTON); + default: + NOTREACHED(); + return L""; + } + } + + virtual int GetDefaultDialogButton() const { + return MessageBoxFlags::DIALOGBUTTON_CANCEL; + } + + virtual bool Accept() { + crx_installer_->ContinueInstall(); + return true; + } + + virtual bool Cancel() { + crx_installer_->AbortInstall(); + return true; + } + + + // WindowDelegate + virtual bool IsModal() const { return true; } + virtual std::wstring GetWindowTitle() const { + return l10n_util::GetString(IDS_EXTENSION_PROMPT_TITLE); + } + virtual views::View* GetContentsView() { return this; } + + // View + virtual gfx::Size GetPreferredSize() { + int width = kRightColumnWidth + kPanelHorizMargin + kPanelHorizMargin; + + if (icon_) { + width += Extension::EXTENSION_ICON_LARGE; + width += kPanelHorizMargin; + } + + int height = kPanelVertMargin * 2; + height += heading_->GetHeightForWidth(kRightColumnWidth); + height += kPanelVertMargin; + height += warning_->GetHeightForWidth(kRightColumnWidth); + height += kPanelVertMargin; + height += severe_->GetHeightForWidth(kRightColumnWidth); + height += kPanelVertMargin; + + return gfx::Size(width, std::max(height, + static_cast<int>(Extension::EXTENSION_ICON_LARGE))); + } + + virtual void Layout() { + int x = kPanelHorizMargin; + int y = kPanelVertMargin; + + if (icon_) { + icon_->SetBounds(x, y, Extension::EXTENSION_ICON_LARGE, + Extension::EXTENSION_ICON_LARGE); + x += Extension::EXTENSION_ICON_LARGE; + x += kPanelHorizMargin; + } + + heading_->SizeToFit(kRightColumnWidth); + heading_->SetX(x); + heading_->SetY(y); + y += heading_->height(); + + y += kPanelVertMargin; + + warning_->SizeToFit(kRightColumnWidth); + warning_->SetX(x); + warning_->SetY(y); + y += warning_->height(); + + y += kPanelVertMargin; + + severe_->SizeToFit(kRightColumnWidth); + severe_->SetX(x); + severe_->SetY(y); + y += severe_->height(); + } + + scoped_refptr<CrxInstaller> crx_installer_; + views::ImageView* icon_; + views::Label* heading_; + views::Label* warning_; + views::Label* severe_; + + DISALLOW_COPY_AND_ASSIGN(InstallDialogContent); +}; + +} // namespace + +void ExtensionInstallUI::ShowExtensionInstallPrompt(Profile* profile, + CrxInstaller* crx_installer, + Extension* extension, + SkBitmap* icon) { + Browser* browser = BrowserList::GetLastActiveWithProfile(profile); + if (!browser) { + crx_installer->AbortInstall(); + return; + } + + BrowserWindow* window = browser->window(); + if (!window) { + crx_installer->AbortInstall(); + return; + } + + views::Window::CreateChromeWindow(window->GetNativeHandle(), gfx::Rect(), + new InstallDialogContent(crx_installer, extension, icon))->Show(); +} diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc index 674cdb6..bed40dc 100644 --- a/chrome/browser/views/location_bar_view.cc +++ b/chrome/browser/views/location_bar_view.cc @@ -1263,18 +1263,22 @@ LocationBarView::PageActionImageView::PageActionImageView( page_action_(page_action), current_tab_id_(-1), tooltip_(page_action_->name()) { + Extension* extension = profile->GetExtensionsService()->GetExtensionById( + page_action->extension_id()); + DCHECK(extension); + // Load the images this view needs asynchronously on the file thread. We'll // get a call back into OnImageLoaded if the image loads successfully. If not, // the ImageView will have no image and will not appear in the Omnibox. DCHECK(!page_action->icon_paths().empty()); - const std::vector<FilePath>& icon_paths = page_action->icon_paths(); + const std::vector<std::string>& icon_paths = page_action->icon_paths(); page_action_icons_.resize(icon_paths.size()); int index = 0; MessageLoop* file_loop = g_browser_process->file_thread()->message_loop(); tracker_ = new ImageLoadingTracker(this, icon_paths.size()); - for (std::vector<FilePath>::const_iterator iter = icon_paths.begin(); + for (std::vector<std::string>::const_iterator iter = icon_paths.begin(); iter != icon_paths.end(); ++iter) { - FilePath path = *iter; + FilePath path = extension->GetResourcePath(*iter); file_loop->PostTask(FROM_HERE, new LoadImageTask(tracker_, path, index++)); } } |