Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / apps / app_info_dialog / app_info_summary_panel.cc
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.h"
6
7 #include <vector>
8
9 #include "base/callback_forward.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/task_runner_util.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_util.h"
16 #include "chrome/browser/extensions/launch_util.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "extensions/browser/extension_prefs.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/manifest.h"
25 #include "extensions/common/manifest_handlers/shared_module_info.h"
26 #include "extensions/common/manifest_url_handlers.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/models/combobox_model.h"
29 #include "ui/base/text/bytes_formatting.h"
30 #include "ui/views/controls/combobox/combobox.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/controls/link.h"
33 #include "ui/views/layout/box_layout.h"
34 #include "ui/views/layout/layout_constants.h"
35 #include "ui/views/view.h"
36
37 // A model for a combobox selecting the launch options for a hosted app.
38 // Displays different options depending on the host OS.
39 class LaunchOptionsComboboxModel : public ui::ComboboxModel {
40  public:
41   LaunchOptionsComboboxModel();
42   ~LaunchOptionsComboboxModel() override;
43
44   extensions::LaunchType GetLaunchTypeAtIndex(int index) const;
45   int GetIndexForLaunchType(extensions::LaunchType launch_type) const;
46
47   // Overridden from ui::ComboboxModel:
48   int GetItemCount() const override;
49   base::string16 GetItemAt(int index) override;
50
51  private:
52   // A list of the launch types available in the combobox, in order.
53   std::vector<extensions::LaunchType> launch_types_;
54
55   // A list of the messages to display in the combobox, in order. The indexes in
56   // this list correspond to the indexes in launch_types_.
57   std::vector<base::string16> launch_type_messages_;
58 };
59
60 LaunchOptionsComboboxModel::LaunchOptionsComboboxModel() {
61   if (extensions::util::IsStreamlinedHostedAppsEnabled()) {
62     // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
63     // LAUNCH_TYPE_REGULAR.
64     // TODO(sashab): Use a checkbox for this choice instead of combobox.
65     launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
66     launch_type_messages_.push_back(
67         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_TAB));
68
69     // Although LAUNCH_TYPE_WINDOW doesn't work on Mac, the streamlined hosted
70     // apps flag isn't available on Mac, so we must be on a non-Mac OS.
71     launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
72     launch_type_messages_.push_back(
73         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
74   } else {
75     launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
76     launch_type_messages_.push_back(
77         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
78
79     launch_types_.push_back(extensions::LAUNCH_TYPE_PINNED);
80     launch_type_messages_.push_back(
81         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
82
83 #if defined(OS_MACOSX)
84     // Mac does not support standalone web app browser windows or maximize.
85     launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
86     launch_type_messages_.push_back(
87         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
88 #else
89     launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
90     launch_type_messages_.push_back(
91         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
92
93     // Even though the launch type is Full Screen, it is more accurately
94     // described as Maximized in non-Mac OSs.
95     launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
96     launch_type_messages_.push_back(
97         l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED));
98 #endif
99   }
100 }
101
102 LaunchOptionsComboboxModel::~LaunchOptionsComboboxModel() {
103 }
104
105 extensions::LaunchType LaunchOptionsComboboxModel::GetLaunchTypeAtIndex(
106     int index) const {
107   return launch_types_[index];
108 }
109
110 int LaunchOptionsComboboxModel::GetIndexForLaunchType(
111     extensions::LaunchType launch_type) const {
112   for (size_t i = 0; i < launch_types_.size(); i++) {
113     if (launch_types_[i] == launch_type) {
114       return i;
115     }
116   }
117   // If the requested launch type is not available, just select the first one.
118   LOG(WARNING) << "Unavailable launch type " << launch_type << " selected.";
119   return 0;
120 }
121
122 int LaunchOptionsComboboxModel::GetItemCount() const {
123   return launch_types_.size();
124 }
125
126 base::string16 LaunchOptionsComboboxModel::GetItemAt(int index) {
127   return launch_type_messages_[index];
128 }
129
130 AppInfoSummaryPanel::AppInfoSummaryPanel(Profile* profile,
131                                          const extensions::Extension* app)
132     : AppInfoPanel(profile, app),
133       size_value_(NULL),
134       homepage_link_(NULL),
135       licenses_link_(NULL),
136       launch_options_combobox_(NULL),
137       weak_ptr_factory_(this) {
138   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
139                                         0,
140                                         0,
141                                         views::kRelatedControlVerticalSpacing));
142
143   AddSubviews();
144 }
145
146 AppInfoSummaryPanel::~AppInfoSummaryPanel() {
147   // Destroy view children before their models.
148   RemoveAllChildViews(true);
149 }
150
151 void AppInfoSummaryPanel::AddDescriptionAndLinksControl(
152     views::View* vertical_stack) {
153   views::View* description_and_labels_stack = new views::View();
154   description_and_labels_stack->SetLayoutManager(
155       new views::BoxLayout(views::BoxLayout::kVertical,
156                            0,
157                            0,
158                            views::kRelatedControlSmallVerticalSpacing));
159
160   if (!app_->description().empty()) {
161     // TODO(sashab): Clip the app's description to 4 lines, and use Label's
162     // built-in elide behavior to add ellipses at the end: crbug.com/358053
163     const size_t max_length = 400;
164     base::string16 text = base::UTF8ToUTF16(app_->description());
165     if (text.length() > max_length) {
166       text = text.substr(0, max_length);
167       text += base::ASCIIToUTF16(" ... ");
168     }
169
170     views::Label* description_label = new views::Label(text);
171     description_label->SetMultiLine(true);
172     description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
173     description_and_labels_stack->AddChildView(description_label);
174   }
175
176   if (CanShowAppHomePage()) {
177     homepage_link_ = new views::Link(
178         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_HOMEPAGE_LINK));
179     homepage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
180     homepage_link_->set_listener(this);
181     description_and_labels_stack->AddChildView(homepage_link_);
182   }
183
184   if (CanDisplayLicenses()) {
185     licenses_link_ = new views::Link(
186         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LICENSES_BUTTON_TEXT));
187     licenses_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
188     licenses_link_->set_listener(this);
189     description_and_labels_stack->AddChildView(licenses_link_);
190   }
191
192   vertical_stack->AddChildView(description_and_labels_stack);
193 }
194
195 void AppInfoSummaryPanel::AddDetailsControl(views::View* vertical_stack) {
196   // Component apps have no details.
197   if (app_->location() == extensions::Manifest::COMPONENT)
198     return;
199
200   views::View* details_list =
201       CreateVerticalStack(views::kRelatedControlSmallVerticalSpacing);
202
203   // Add the size.
204   views::Label* size_title = new views::Label(
205       l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LABEL));
206   size_title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
207
208   size_value_ = new views::Label(
209       l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LOADING_LABEL));
210   size_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
211   StartCalculatingAppSize();
212
213   details_list->AddChildView(CreateKeyValueField(size_title, size_value_));
214
215   // The version doesn't make sense for bookmark apps.
216   if (!app_->from_bookmark()) {
217     views::Label* version_title = new views::Label(
218         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_VERSION_LABEL));
219     version_title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
220
221     views::Label* version_value =
222         new views::Label(base::UTF8ToUTF16(app_->VersionString()));
223     version_value->SetHorizontalAlignment(gfx::ALIGN_LEFT);
224
225     details_list->AddChildView(
226         CreateKeyValueField(version_title, version_value));
227   }
228
229   vertical_stack->AddChildView(details_list);
230 }
231
232 void AppInfoSummaryPanel::AddLaunchOptionControl(views::View* vertical_stack) {
233   if (!CanSetLaunchType())
234     return;
235
236   launch_options_combobox_model_.reset(new LaunchOptionsComboboxModel());
237   launch_options_combobox_ =
238       new views::Combobox(launch_options_combobox_model_.get());
239   launch_options_combobox_->set_listener(this);
240   launch_options_combobox_->SetSelectedIndex(
241       launch_options_combobox_model_->GetIndexForLaunchType(GetLaunchType()));
242
243   vertical_stack->AddChildView(launch_options_combobox_);
244 }
245
246 void AppInfoSummaryPanel::AddSubviews() {
247   AddChildView(CreateHeading(
248       l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_APP_OVERVIEW_TITLE)));
249
250   views::View* vertical_stack =
251       CreateVerticalStack(views::kUnrelatedControlVerticalSpacing);
252   AddChildView(vertical_stack);
253
254   AddDescriptionAndLinksControl(vertical_stack);
255   AddDetailsControl(vertical_stack);
256   AddLaunchOptionControl(vertical_stack);
257 }
258
259 void AppInfoSummaryPanel::OnPerformAction(views::Combobox* combobox) {
260   if (combobox == launch_options_combobox_) {
261     SetLaunchType(launch_options_combobox_model_->GetLaunchTypeAtIndex(
262         launch_options_combobox_->selected_index()));
263   } else {
264     NOTREACHED();
265   }
266 }
267
268 void AppInfoSummaryPanel::LinkClicked(views::Link* source, int event_flags) {
269   if (source == homepage_link_) {
270     ShowAppHomePage();
271   } else if (source == licenses_link_) {
272     DisplayLicenses();
273   } else {
274     NOTREACHED();
275   }
276 }
277
278 void AppInfoSummaryPanel::StartCalculatingAppSize() {
279   base::PostTaskAndReplyWithResult(
280       content::BrowserThread::GetBlockingPool(),
281       FROM_HERE,
282       base::Bind(&base::ComputeDirectorySize, app_->path()),
283       base::Bind(&AppInfoSummaryPanel::OnAppSizeCalculated, AsWeakPtr()));
284 }
285
286 void AppInfoSummaryPanel::OnAppSizeCalculated(int64 app_size_in_bytes) {
287   const int one_mebibyte_in_bytes = 1024 * 1024;
288   if (app_size_in_bytes < one_mebibyte_in_bytes) {
289     size_value_->SetText(
290         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_SMALL_LABEL));
291   } else {
292     size_value_->SetText(ui::FormatBytesWithUnits(
293         app_size_in_bytes, ui::DATA_UNITS_MEBIBYTE, true));
294   }
295 }
296
297 extensions::LaunchType AppInfoSummaryPanel::GetLaunchType() const {
298   return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
299                                    app_);
300 }
301
302 void AppInfoSummaryPanel::SetLaunchType(
303     extensions::LaunchType launch_type) const {
304   DCHECK(CanSetLaunchType());
305   ExtensionService* service =
306       extensions::ExtensionSystem::Get(profile_)->extension_service();
307   extensions::SetLaunchType(service, app_->id(), launch_type);
308 }
309
310 bool AppInfoSummaryPanel::CanSetLaunchType() const {
311   // V2 apps and extensions don't have a launch type, and neither does the
312   // Chrome app.
313   return !app_->is_platform_app() && !app_->is_extension() &&
314          app_->id() != extension_misc::kChromeAppId;
315 }
316 void AppInfoSummaryPanel::ShowAppHomePage() {
317   DCHECK(CanShowAppHomePage());
318   OpenLink(extensions::ManifestURL::GetHomepageURL(app_));
319   Close();
320 }
321
322 bool AppInfoSummaryPanel::CanShowAppHomePage() const {
323   return extensions::ManifestURL::SpecifiedHomepageURL(app_);
324 }
325
326 void AppInfoSummaryPanel::DisplayLicenses() {
327   DCHECK(CanDisplayLicenses());
328   for (const auto& license_url : GetLicenseUrls())
329     OpenLink(license_url);
330   Close();
331 }
332
333 bool AppInfoSummaryPanel::CanDisplayLicenses() const {
334   return !GetLicenseUrls().empty();
335 }
336
337 const std::vector<GURL> AppInfoSummaryPanel::GetLicenseUrls() const {
338   if (!extensions::SharedModuleInfo::ImportsModules(app_))
339     return std::vector<GURL>();
340
341   std::vector<GURL> license_urls;
342   ExtensionService* service =
343       extensions::ExtensionSystem::Get(profile_)->extension_service();
344   DCHECK(service);
345   const std::vector<extensions::SharedModuleInfo::ImportInfo>& imports =
346       extensions::SharedModuleInfo::GetImports(app_);
347
348   for (const auto& shared_module : imports) {
349     const extensions::Extension* imported_module =
350         service->GetExtensionById(shared_module.extension_id, true);
351     DCHECK(imported_module);
352
353     GURL about_page = extensions::ManifestURL::GetAboutPage(imported_module);
354     if (about_page != GURL::EmptyGURL())
355       license_urls.push_back(about_page);
356   }
357   return license_urls;
358 }