Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h" | 5 #include "chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h" |
| 6 | 6 |
| 7 #include "base/strings/string16.h" | 7 #include "base/strings/string16.h" |
| 8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
| 9 #include "chrome/browser/chrome_notification_types.h" | |
| 10 #include "chrome/browser/extensions/app_icon_loader_impl.h" | |
| 11 #include "chrome/browser/extensions/bookmark_app_helper.h" | |
| 12 #include "chrome/browser/extensions/extension_service.h" | |
| 13 #include "chrome/browser/extensions/launch_util.h" | |
| 14 #include "chrome/browser/profiles/profile.h" | |
| 15 #include "chrome/browser/ui/app_list/app_list_service.h" | |
| 16 #include "chrome/browser/ui/app_list/app_list_util.h" | |
| 17 #include "chrome/browser/ui/browser_navigator.h" | |
| 18 #include "chrome/browser/ui/host_desktop.h" | |
| 19 #include "chrome/common/extensions/extension_constants.h" | |
| 20 #include "chrome/common/url_constants.h" | |
| 21 #include "chrome/grit/generated_resources.h" | 9 #include "chrome/grit/generated_resources.h" |
| 22 #include "content/public/browser/notification_service.h" | |
| 23 #include "content/public/browser/web_contents.h" | 10 #include "content/public/browser/web_contents.h" |
| 24 #include "extensions/browser/extension_prefs.h" | |
| 25 #include "extensions/browser/extension_registry.h" | |
| 26 #include "extensions/browser/extension_system.h" | |
| 27 #include "extensions/browser/pref_names.h" | |
| 28 #include "extensions/browser/uninstall_reason.h" | |
| 29 #include "extensions/common/constants.h" | 11 #include "extensions/common/constants.h" |
| 30 #include "ui/base/l10n/l10n_util.h" | 12 #include "ui/base/l10n/l10n_util.h" |
| 31 #include "ui/base/resource/resource_bundle.h" | 13 #include "ui/base/resource/resource_bundle.h" |
| 32 #include "ui/events/keycodes/keyboard_codes.h" | 14 #include "ui/events/keycodes/keyboard_codes.h" |
| 15 #include "ui/gfx/image/image_skia.h" | |
| 16 #include "ui/gfx/image/image_skia_source.h" | |
| 33 #include "ui/views/controls/button/checkbox.h" | 17 #include "ui/views/controls/button/checkbox.h" |
| 34 #include "ui/views/controls/button/label_button.h" | 18 #include "ui/views/controls/button/label_button.h" |
| 35 #include "ui/views/controls/image_view.h" | 19 #include "ui/views/controls/image_view.h" |
| 36 #include "ui/views/controls/label.h" | 20 #include "ui/views/controls/label.h" |
| 37 #include "ui/views/controls/textfield/textfield.h" | 21 #include "ui/views/controls/textfield/textfield.h" |
| 38 #include "ui/views/layout/grid_layout.h" | 22 #include "ui/views/layout/grid_layout.h" |
| 39 #include "ui/views/layout/layout_constants.h" | 23 #include "ui/views/layout/layout_constants.h" |
| 40 #include "ui/views/widget/widget.h" | 24 #include "ui/views/widget/widget.h" |
| 41 | 25 |
| 42 using views::ColumnSet; | 26 using views::ColumnSet; |
| 43 using views::GridLayout; | 27 using views::GridLayout; |
| 44 | 28 |
| 45 namespace { | 29 namespace { |
| 46 | 30 |
| 47 // Minimum width of the the bubble. | 31 // Minimum width of the the bubble. |
| 48 const int kMinBubbleWidth = 300; | 32 const int kMinBubbleWidth = 300; |
| 49 // Minimum width of the the textfield. | 33 // Minimum width of the the textfield. |
| 50 const int kMinTextfieldWidth = 200; | 34 const int kMinTextfieldWidth = 200; |
| 51 // Size of the icon. | 35 // Size of the icon. |
| 52 const int kIconSize = extension_misc::EXTENSION_ICON_MEDIUM; | 36 const int kIconSize = extension_misc::EXTENSION_ICON_MEDIUM; |
| 53 | 37 |
| 54 ExtensionService* GetExtensionService(Profile* profile) { | 38 class WebAppInfoImageSource : public gfx::ImageSkiaSource { |
| 55 return extensions::ExtensionSystem::Get(profile)->extension_service(); | 39 public: |
| 56 } | 40 WebAppInfoImageSource(int dip_size, const WebApplicationInfo& info) |
| 41 : dip_size_(dip_size), info_(info) {} | |
| 42 ~WebAppInfoImageSource() override {} | |
| 43 | |
| 44 private: | |
| 45 gfx::ImageSkiaRep GetImageForScale(float scale) override { | |
| 46 int size = static_cast<int>(dip_size_ * scale); | |
| 47 for (auto icon_info : info_.icons) { | |
| 48 if (icon_info.width == size) { | |
| 49 return gfx::ImageSkiaRep(icon_info.data, scale); | |
| 50 } | |
| 51 } | |
| 52 return gfx::ImageSkiaRep(); | |
| 53 } | |
| 54 | |
| 55 int dip_size_; | |
| 56 WebApplicationInfo info_; | |
| 57 }; | |
| 57 | 58 |
| 58 } // namespace | 59 } // namespace |
| 59 | 60 |
| 60 BookmarkAppBubbleView* BookmarkAppBubbleView::bookmark_app_bubble_ = NULL; | |
| 61 | |
| 62 BookmarkAppBubbleView::~BookmarkAppBubbleView() { | 61 BookmarkAppBubbleView::~BookmarkAppBubbleView() { |
| 63 } | 62 } |
| 64 | 63 |
| 65 // static | 64 // static |
| 66 void BookmarkAppBubbleView::ShowBubble(views::View* anchor_view, | 65 void BookmarkAppBubbleView::ShowBubble( |
| 67 Profile* profile, | 66 views::View* anchor_view, |
| 68 const WebApplicationInfo& web_app_info, | 67 const WebApplicationInfo& web_app_info, |
| 69 const std::string& extension_id) { | 68 const BrowserWindow::ShowBookmarkAppBubbleCallback& callback) { |
|
calamity
2015/02/03 07:29:49
There's no protection here against showing 2 dialo
benwells
2015/02/03 08:54:13
Yes. I don't think there is any need for this prot
| |
| 70 if (bookmark_app_bubble_ != NULL) | 69 BookmarkAppBubbleView* bookmark_app_bubble = |
| 71 return; | 70 new BookmarkAppBubbleView(anchor_view, web_app_info, callback); |
| 72 | 71 views::BubbleDelegateView::CreateBubble(bookmark_app_bubble)->Show(); |
| 73 bookmark_app_bubble_ = new BookmarkAppBubbleView( | |
| 74 anchor_view, profile, web_app_info, extension_id); | |
| 75 views::BubbleDelegateView::CreateBubble(bookmark_app_bubble_)->Show(); | |
| 76 // Select the entire title textfield contents when the bubble is first shown. | 72 // Select the entire title textfield contents when the bubble is first shown. |
| 77 bookmark_app_bubble_->title_tf_->SelectAll(true); | 73 bookmark_app_bubble->title_tf_->SelectAll(true); |
| 78 bookmark_app_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); | 74 bookmark_app_bubble->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); |
| 79 } | 75 } |
| 80 | 76 |
| 81 BookmarkAppBubbleView::BookmarkAppBubbleView( | 77 BookmarkAppBubbleView::BookmarkAppBubbleView( |
| 82 views::View* anchor_view, | 78 views::View* anchor_view, |
| 83 Profile* profile, | |
| 84 const WebApplicationInfo& web_app_info, | 79 const WebApplicationInfo& web_app_info, |
| 85 const std::string& extension_id) | 80 const BrowserWindow::ShowBookmarkAppBubbleCallback& callback) |
| 86 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), | 81 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), |
| 87 profile_(profile), | |
| 88 web_app_info_(web_app_info), | 82 web_app_info_(web_app_info), |
| 89 extension_id_(extension_id), | 83 user_accepted_(false), |
| 84 callback_(callback), | |
| 90 add_button_(NULL), | 85 add_button_(NULL), |
| 91 cancel_button_(NULL), | 86 cancel_button_(NULL), |
| 92 open_as_window_checkbox_(NULL), | 87 open_as_window_checkbox_(NULL), |
| 93 title_tf_(NULL), | 88 title_tf_(NULL) { |
| 94 remove_app_(true), | |
| 95 app_icon_loader_(new extensions::AppIconLoaderImpl(profile, | |
| 96 kIconSize, | |
| 97 this)) { | |
| 98 const SkColor background_color = GetNativeTheme()->GetSystemColor( | 89 const SkColor background_color = GetNativeTheme()->GetSystemColor( |
| 99 ui::NativeTheme::kColorId_DialogBackground); | 90 ui::NativeTheme::kColorId_DialogBackground); |
| 100 set_arrow(views::BubbleBorder::TOP_CENTER); | 91 set_arrow(views::BubbleBorder::TOP_CENTER); |
| 101 set_color(background_color); | 92 set_color(background_color); |
| 102 set_background(views::Background::CreateSolidBackground(background_color)); | 93 set_background(views::Background::CreateSolidBackground(background_color)); |
| 103 set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0)); | 94 set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0)); |
| 104 } | 95 } |
| 105 | 96 |
| 106 void BookmarkAppBubbleView::Init() { | 97 void BookmarkAppBubbleView::Init() { |
| 107 views::Label* title_label = new views::Label( | 98 views::Label* title_label = new views::Label( |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 164 GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); | 155 GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); |
| 165 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); | 156 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing); |
| 166 cs->AddColumn( | 157 cs->AddColumn( |
| 167 GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); | 158 GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); |
| 168 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); | 159 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); |
| 169 | 160 |
| 170 layout->StartRow(0, TITLE_COLUMN_SET_ID); | 161 layout->StartRow(0, TITLE_COLUMN_SET_ID); |
| 171 layout->AddView(title_label); | 162 layout->AddView(title_label); |
| 172 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); | 163 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| 173 | 164 |
| 174 const extensions::Extension* extension = | |
| 175 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById( | |
| 176 extension_id_, extensions::ExtensionRegistry::EVERYTHING); | |
| 177 | |
| 178 layout->StartRow(0, TITLE_TEXT_COLUMN_SET_ID); | 165 layout->StartRow(0, TITLE_TEXT_COLUMN_SET_ID); |
| 179 icon_image_view_ = new views::ImageView(); | 166 icon_image_view_ = new views::ImageView(); |
| 180 icon_image_view_->SetImageSize(gfx::Size(kIconSize, kIconSize)); | 167 |
| 168 gfx::Size image_size(kIconSize, kIconSize); | |
| 169 gfx::ImageSkia image(new WebAppInfoImageSource(kIconSize, web_app_info_), | |
| 170 image_size); | |
| 171 icon_image_view_->SetImageSize(image_size); | |
| 172 icon_image_view_->SetImage(image); | |
| 181 layout->AddView(icon_image_view_); | 173 layout->AddView(icon_image_view_); |
| 182 app_icon_loader_->FetchImage(extension_id_); | |
| 183 | 174 |
| 184 title_tf_ = new views::Textfield(); | 175 title_tf_ = new views::Textfield(); |
| 185 title_tf_->SetText(extension ? base::UTF8ToUTF16(extension->name()) | 176 title_tf_->SetText(web_app_info_.title); |
| 186 : web_app_info_.title); | |
| 187 layout->AddView(title_tf_); | 177 layout->AddView(title_tf_); |
| 188 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); | 178 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
| 189 | 179 |
| 190 layout->StartRow(0, CONTENT_COLUMN_SET_ID); | 180 layout->StartRow(0, CONTENT_COLUMN_SET_ID); |
| 191 open_as_window_checkbox_ = new views::Checkbox( | 181 open_as_window_checkbox_ = new views::Checkbox( |
| 192 l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW)); | 182 l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW)); |
| 193 open_as_window_checkbox_->SetChecked( | 183 open_as_window_checkbox_->SetChecked(web_app_info_.open_as_window); |
| 194 profile_->GetPrefs()->GetInteger( | |
| 195 extensions::pref_names::kBookmarkAppCreationLaunchType) == | |
| 196 extensions::LAUNCH_TYPE_WINDOW); | |
| 197 layout->AddView(open_as_window_checkbox_); | 184 layout->AddView(open_as_window_checkbox_); |
| 198 layout->AddView(add_button_); | 185 layout->AddView(add_button_); |
| 199 layout->AddView(cancel_button_); | 186 layout->AddView(cancel_button_); |
| 200 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); | 187 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); |
| 201 | 188 |
| 202 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); | 189 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); |
| 203 } | 190 } |
| 204 | 191 |
| 205 views::View* BookmarkAppBubbleView::GetInitiallyFocusedView() { | 192 views::View* BookmarkAppBubbleView::GetInitiallyFocusedView() { |
| 206 return title_tf_; | 193 return title_tf_; |
| 207 } | 194 } |
| 208 | 195 |
| 209 void BookmarkAppBubbleView::WindowClosing() { | 196 void BookmarkAppBubbleView::WindowClosing() { |
| 210 // We have to reset |bookmark_app_bubble_| here, not in our destructor, | 197 callback_.Run(user_accepted_, web_app_info_); |
| 211 // because we'll be destroyed asynchronously and the shown state will be | |
| 212 // checked before then. | |
| 213 DCHECK_EQ(bookmark_app_bubble_, this); | |
| 214 bookmark_app_bubble_ = NULL; | |
| 215 | |
| 216 if (remove_app_) { | |
| 217 GetExtensionService(profile_) | |
| 218 ->UninstallExtension(extension_id_, | |
| 219 extensions::UNINSTALL_REASON_INSTALL_CANCELED, | |
| 220 base::Bind(&base::DoNothing), | |
| 221 NULL); | |
| 222 } else { | |
| 223 ApplyEdits(); | |
| 224 } | |
| 225 } | 198 } |
| 226 | 199 |
| 227 bool BookmarkAppBubbleView::AcceleratorPressed( | 200 bool BookmarkAppBubbleView::AcceleratorPressed( |
| 228 const ui::Accelerator& accelerator) { | 201 const ui::Accelerator& accelerator) { |
| 229 if (accelerator.key_code() == ui::VKEY_RETURN) { | 202 if (accelerator.key_code() == ui::VKEY_RETURN) { |
| 230 HandleButtonPressed(add_button_); | 203 HandleButtonPressed(add_button_); |
| 231 } | 204 } |
| 232 | 205 |
| 233 return BubbleDelegateView::AcceleratorPressed(accelerator); | 206 return BubbleDelegateView::AcceleratorPressed(accelerator); |
| 234 } | 207 } |
| 235 | 208 |
| 236 gfx::Size BookmarkAppBubbleView::GetMinimumSize() const { | 209 gfx::Size BookmarkAppBubbleView::GetMinimumSize() const { |
| 237 gfx::Size size(views::BubbleDelegateView::GetPreferredSize()); | 210 gfx::Size size(views::BubbleDelegateView::GetPreferredSize()); |
| 238 size.SetToMax(gfx::Size(kMinBubbleWidth, 0)); | 211 size.SetToMax(gfx::Size(kMinBubbleWidth, 0)); |
| 239 return size; | 212 return size; |
| 240 } | 213 } |
| 241 | 214 |
| 242 void BookmarkAppBubbleView::ButtonPressed(views::Button* sender, | 215 void BookmarkAppBubbleView::ButtonPressed(views::Button* sender, |
| 243 const ui::Event& event) { | 216 const ui::Event& event) { |
| 244 HandleButtonPressed(sender); | 217 HandleButtonPressed(sender); |
| 245 } | 218 } |
| 246 | 219 |
| 247 void BookmarkAppBubbleView::SetAppImage(const std::string& id, | |
| 248 const gfx::ImageSkia& image) { | |
| 249 DCHECK_EQ(extension_id_, id); | |
| 250 icon_image_view_->SetImage(image); | |
| 251 } | |
| 252 | |
| 253 void BookmarkAppBubbleView::HandleButtonPressed(views::Button* sender) { | 220 void BookmarkAppBubbleView::HandleButtonPressed(views::Button* sender) { |
| 254 // Unset |remove_app_| so we don't delete the bookmark after the window | 221 if (sender == add_button_) { |
| 255 // closes. | 222 user_accepted_ = true; |
| 256 if (sender == add_button_) | 223 web_app_info_.title = title_tf_->text(); |
| 257 remove_app_ = false; | 224 web_app_info_.open_as_window = open_as_window_checkbox_->checked(); |
| 225 } | |
| 258 | 226 |
| 259 GetWidget()->Close(); | 227 GetWidget()->Close(); |
| 260 } | 228 } |
| 261 | |
| 262 void BookmarkAppBubbleView::ApplyEdits() { | |
| 263 // Set the launch type based on the checkbox. | |
| 264 extensions::LaunchType launch_type = open_as_window_checkbox_->checked() | |
| 265 ? extensions::LAUNCH_TYPE_WINDOW | |
| 266 : extensions::LAUNCH_TYPE_REGULAR; | |
| 267 profile_->GetPrefs()->SetInteger( | |
| 268 extensions::pref_names::kBookmarkAppCreationLaunchType, launch_type); | |
| 269 extensions::SetLaunchType(profile_, extension_id_, launch_type); | |
| 270 | |
| 271 const extensions::Extension* extension = | |
| 272 extensions::ExtensionRegistry::Get(profile_)->GetExtensionById( | |
| 273 extension_id_, extensions::ExtensionRegistry::EVERYTHING); | |
| 274 | |
| 275 if (!extension) | |
| 276 return; | |
| 277 | |
| 278 if (base::UTF8ToUTF16(extension->name()) != title_tf_->text()) { | |
| 279 // Reinstall the app with an updated name. | |
| 280 WebApplicationInfo install_info(web_app_info_); | |
| 281 install_info.title = title_tf_->text(); | |
| 282 | |
| 283 // This will asynchronously reload the extension, causing the Extension* | |
| 284 // we have to be destroyed. The extension ID will stay the same so that is | |
| 285 // used later on to highlight the app. | |
| 286 extensions::CreateOrUpdateBookmarkApp(GetExtensionService(profile_), | |
| 287 &install_info); | |
| 288 } | |
| 289 | |
| 290 // As the extension could be destroyed after this point, set it to null to | |
| 291 // prevent anyone trying to use it in future. | |
| 292 extension = nullptr; | |
| 293 | |
| 294 // Show the newly installed app in the app launcher or chrome://apps. | |
| 295 Profile* current_profile = profile_->GetOriginalProfile(); | |
| 296 if (IsAppLauncherEnabled()) { | |
| 297 chrome::HostDesktopType desktop = chrome::GetHostDesktopTypeForNativeWindow( | |
| 298 GetWidget()->GetNativeWindow()); | |
| 299 AppListService::Get(desktop) | |
| 300 ->ShowForAppInstall(current_profile, extension_id_, false); | |
| 301 return; | |
| 302 } | |
| 303 | |
| 304 chrome::NavigateParams params(current_profile, | |
| 305 GURL(chrome::kChromeUIAppsURL), | |
| 306 ui::PAGE_TRANSITION_LINK); | |
| 307 params.disposition = SINGLETON_TAB; | |
| 308 chrome::Navigate(¶ms); | |
| 309 | |
| 310 content::NotificationService::current()->Notify( | |
| 311 chrome::NOTIFICATION_APP_INSTALLED_TO_NTP, | |
| 312 content::Source<content::WebContents>(params.target_contents), | |
| 313 content::Details<const std::string>(&extension_id_)); | |
| 314 } | |
| OLD | NEW |