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 |