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/chromeos/login/ui/simple_web_view_dialog.h"
6 
7 #include "base/macros.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/app/chrome_command_ids.h"
10 #include "chrome/browser/chromeos/login/helper.h"
11 #include "chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h"
12 #include "chrome/browser/command_updater_impl.h"
13 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ssl/security_state_tab_helper.h"
16 #include "chrome/browser/themes/theme_properties.h"
17 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
20 #include "chrome/browser/ui/view_ids.h"
21 #include "chrome/browser/ui/views/chrome_layout_provider.h"
22 #include "chrome/browser/ui/views/toolbar/reload_button.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "chrome/grit/theme_resources.h"
25 #include "components/omnibox/browser/location_bar_model_impl.h"
26 #include "components/password_manager/core/browser/password_manager.h"
27 #include "components/strings/grit/components_strings.h"
28 #include "content/public/browser/navigation_controller.h"
29 #include "content/public/browser/navigation_entry.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/common/content_constants.h"
32 #include "ipc/ipc_message.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/theme_provider.h"
35 #include "ui/views/background.h"
36 #include "ui/views/controls/webview/webview.h"
37 #include "ui/views/layout/grid_layout.h"
38 #include "ui/views/view.h"
39 #include "ui/views/widget/widget.h"
40 #include "ui/views/widget/widget_delegate.h"
41 
42 using content::WebContents;
43 using views::GridLayout;
44 
45 namespace {
46 
47 const int kLocationBarHeight = 35;
48 
49 // Margin between screen edge and SimpleWebViewDialog border.
50 const int kExternalMargin = 60;
51 
52 // Margin between WebView and SimpleWebViewDialog border.
53 const int kInnerMargin = 2;
54 
55 const SkColor kDialogColor = SK_ColorWHITE;
56 
57 class ToolbarRowView : public views::View {
58  public:
ToolbarRowView()59   ToolbarRowView() {
60     SetBackground(views::CreateSolidBackground(kDialogColor));
61   }
62 
~ToolbarRowView()63   ~ToolbarRowView() override {}
64 
Init(std::unique_ptr<views::View> back,std::unique_ptr<views::View> forward,std::unique_ptr<views::View> reload,std::unique_ptr<views::View> location_bar)65   void Init(std::unique_ptr<views::View> back,
66             std::unique_ptr<views::View> forward,
67             std::unique_ptr<views::View> reload,
68             std::unique_ptr<views::View> location_bar) {
69     GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>());
70 
71     const int related_horizontal_spacing =
72         ChromeLayoutProvider::Get()->GetDistanceMetric(
73             views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
74     // Back button.
75     views::ColumnSet* column_set = layout->AddColumnSet(0);
76     column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
77                           GridLayout::ColumnSize::kUsePreferred, 0, 0);
78     column_set->AddPaddingColumn(0, related_horizontal_spacing);
79     // Forward button.
80     column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
81                           GridLayout::ColumnSize::kUsePreferred, 0, 0);
82     column_set->AddPaddingColumn(0, related_horizontal_spacing);
83     // Reload button.
84     column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
85                           GridLayout::ColumnSize::kUsePreferred, 0, 0);
86     column_set->AddPaddingColumn(0, related_horizontal_spacing);
87     // Location bar.
88     column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
89                           GridLayout::ColumnSize::kFixed, kLocationBarHeight,
90                           0);
91     column_set->AddPaddingColumn(0, related_horizontal_spacing);
92 
93     layout->StartRow(0, 0);
94     layout->AddView(std::move(back));
95     layout->AddView(std::move(forward));
96     layout->AddView(std::move(reload));
97     layout->AddView(std::move(location_bar));
98   }
99 
100  private:
101   DISALLOW_COPY_AND_ASSIGN(ToolbarRowView);
102 };
103 
104 }  // namespace
105 
106 namespace chromeos {
107 
108 // Stub implementation of ContentSettingBubbleModelDelegate.
109 class StubBubbleModelDelegate : public ContentSettingBubbleModelDelegate {
110  public:
StubBubbleModelDelegate()111   StubBubbleModelDelegate() {}
~StubBubbleModelDelegate()112   ~StubBubbleModelDelegate() override {}
113 
114  private:
115   // ContentSettingBubbleModelDelegate implementation:
ShowCollectedCookiesDialog(content::WebContents * web_contents)116   void ShowCollectedCookiesDialog(content::WebContents* web_contents) override {
117   }
ShowContentSettingsPage(ContentSettingsType type)118   void ShowContentSettingsPage(ContentSettingsType type) override {}
ShowMediaSettingsPage()119   void ShowMediaSettingsPage() override {}
ShowLearnMorePage(ContentSettingsType type)120   void ShowLearnMorePage(ContentSettingsType type) override {}
121 
122   DISALLOW_COPY_AND_ASSIGN(StubBubbleModelDelegate);
123 };
124 
125 // SimpleWebViewDialog class ---------------------------------------------------
126 
SimpleWebViewDialog(Profile * profile)127 SimpleWebViewDialog::SimpleWebViewDialog(Profile* profile)
128     : profile_(profile),
129       bubble_model_delegate_(new StubBubbleModelDelegate) {
130   command_updater_.reset(new CommandUpdaterImpl(this));
131   command_updater_->UpdateCommandEnabled(IDC_BACK, true);
132   command_updater_->UpdateCommandEnabled(IDC_FORWARD, true);
133   command_updater_->UpdateCommandEnabled(IDC_STOP, true);
134   command_updater_->UpdateCommandEnabled(IDC_RELOAD, true);
135   command_updater_->UpdateCommandEnabled(IDC_RELOAD_BYPASSING_CACHE, true);
136   command_updater_->UpdateCommandEnabled(IDC_RELOAD_CLEARING_CACHE, true);
137 }
138 
~SimpleWebViewDialog()139 SimpleWebViewDialog::~SimpleWebViewDialog() {
140   if (web_view_ && web_view_->web_contents())
141     web_view_->web_contents()->SetDelegate(nullptr);
142 }
143 
StartLoad(const GURL & url)144 void SimpleWebViewDialog::StartLoad(const GURL& url) {
145   if (!web_view_container_)
146     web_view_container_ = std::make_unique<views::WebView>(profile_);
147   web_view_ = web_view_container_.get();
148   web_view_->GetWebContents()->SetDelegate(this);
149   web_view_->LoadInitialURL(url);
150 
151   WebContents* web_contents = web_view_->GetWebContents();
152   DCHECK(web_contents);
153 
154   // Create the password manager that is needed for the proxy.
155   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
156       web_contents,
157       autofill::ChromeAutofillClient::FromWebContents(web_contents));
158 }
159 
Init()160 void SimpleWebViewDialog::Init() {
161   // Create the security state model that the location bar model needs.
162   if (web_view_->GetWebContents())
163     SecurityStateTabHelper::CreateForWebContents(web_view_->GetWebContents());
164   location_bar_model_.reset(
165       new LocationBarModelImpl(this, content::kMaxURLDisplayChars));
166 
167   SetBackground(views::CreateSolidBackground(kDialogColor));
168 
169   // Back/Forward buttons.
170   auto back = std::make_unique<views::ImageButton>(base::BindRepeating(
171       [](CommandUpdater* updater) { updater->ExecuteCommand(IDC_BACK); },
172       command_updater_.get()));
173   back->SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
174                                  ui::EF_MIDDLE_MOUSE_BUTTON);
175   back->SetImageHorizontalAlignment(views::ImageButton::ALIGN_RIGHT);
176   back->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK));
177   back->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
178   back->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
179   back->SetID(VIEW_ID_BACK_BUTTON);
180   back_ = back.get();
181 
182   auto forward = std::make_unique<views::ImageButton>(base::BindRepeating(
183       [](CommandUpdater* updater) { updater->ExecuteCommand(IDC_FORWARD); },
184       command_updater_.get()));
185   forward->SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
186                                     ui::EF_MIDDLE_MOUSE_BUTTON);
187   forward->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD));
188   forward->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
189   forward->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD));
190   forward->SetID(VIEW_ID_FORWARD_BUTTON);
191   forward_ = forward.get();
192 
193   // Location bar.
194   auto location_bar = std::make_unique<LocationBarView>(
195       nullptr, profile_, command_updater_.get(), this, true);
196   location_bar_ = location_bar.get();
197 
198   // Reload button.
199   auto reload = std::make_unique<ReloadButton>(command_updater_.get());
200   reload->SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
201                                    ui::EF_MIDDLE_MOUSE_BUTTON);
202   reload->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_RELOAD));
203   reload->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD));
204   reload->SetID(VIEW_ID_RELOAD_BUTTON);
205   reload_ = reload.get();
206 
207   // Use separate view to setup custom background.
208   auto toolbar_row = std::make_unique<ToolbarRowView>();
209   toolbar_row->Init(std::move(back), std::move(forward), std::move(reload),
210                     std::move(location_bar));
211   // Add the views as child views before the grid layout is installed. This
212   // ensures ownership is more clear.
213   ToolbarRowView* toolbar_row_ptr = AddChildView(std::move(toolbar_row));
214   // Transfer ownership of the `web_view_` from the `web_view_container_`
215   // created in StartLoad() to `this`.
216   AddChildView(std::move(web_view_container_));
217 
218   // Layout.
219   GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>());
220 
221   views::ColumnSet* column_set = layout->AddColumnSet(0);
222   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
223                         GridLayout::ColumnSize::kFixed, 0, 0);
224 
225   column_set = layout->AddColumnSet(1);
226   column_set->AddPaddingColumn(0, kInnerMargin);
227   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
228                         GridLayout::ColumnSize::kFixed, 0, 0);
229   column_set->AddPaddingColumn(0, kInnerMargin);
230 
231   // Setup layout rows.
232   layout->StartRow(0, 0);
233   layout->AddExistingView(toolbar_row_ptr);
234 
235   layout->AddPaddingRow(0, kInnerMargin);
236 
237   layout->StartRow(1, 1);
238   layout->AddExistingView(web_view_);
239   layout->AddPaddingRow(0, kInnerMargin);
240 
241   LoadImages();
242 
243   location_bar_->Init();
244   UpdateReload(web_view_->web_contents()->IsLoading(), true);
245 
246   gfx::Rect bounds(CalculateScreenBounds(gfx::Size()));
247   bounds.Inset(kExternalMargin, kExternalMargin);
248   layout->set_minimum_size(bounds.size());
249 
250   Layout();
251 }
252 
OpenURL(const content::OpenURLParams & params)253 content::WebContents* SimpleWebViewDialog::OpenURL(
254     const content::OpenURLParams& params) {
255   // As there are no Browsers right now, this could not actually ever work.
256   NOTIMPLEMENTED();
257   return nullptr;
258 }
259 
NavigationStateChanged(WebContents * source,content::InvalidateTypes changed_flags)260 void SimpleWebViewDialog::NavigationStateChanged(
261     WebContents* source,
262     content::InvalidateTypes changed_flags) {
263   if (location_bar_) {
264     location_bar_->Update(nullptr);
265     UpdateButtons();
266   }
267 }
268 
LoadingStateChanged(WebContents * source,bool to_different_document)269 void SimpleWebViewDialog::LoadingStateChanged(WebContents* source,
270                                               bool to_different_document) {
271   bool is_loading = source->IsLoading();
272   UpdateReload(is_loading && to_different_document, false);
273   command_updater_->UpdateCommandEnabled(IDC_STOP, is_loading);
274 }
275 
GetWebContents()276 WebContents* SimpleWebViewDialog::GetWebContents() {
277   return nullptr;
278 }
279 
GetLocationBarModel()280 LocationBarModel* SimpleWebViewDialog::GetLocationBarModel() {
281   return location_bar_model_.get();
282 }
283 
GetLocationBarModel() const284 const LocationBarModel* SimpleWebViewDialog::GetLocationBarModel() const {
285   return location_bar_model_.get();
286 }
287 
288 ContentSettingBubbleModelDelegate*
GetContentSettingBubbleModelDelegate()289 SimpleWebViewDialog::GetContentSettingBubbleModelDelegate() {
290   return bubble_model_delegate_.get();
291 }
292 
GetActiveWebContents() const293 content::WebContents* SimpleWebViewDialog::GetActiveWebContents() const {
294   return web_view_->web_contents();
295 }
296 
ExecuteCommandWithDisposition(int id,WindowOpenDisposition)297 void SimpleWebViewDialog::ExecuteCommandWithDisposition(int id,
298                                                         WindowOpenDisposition) {
299   WebContents* web_contents = web_view_->web_contents();
300   switch (id) {
301     case IDC_BACK:
302       if (web_contents->GetController().CanGoBack()) {
303         location_bar_->Revert();
304         web_contents->GetController().GoBack();
305       }
306       break;
307     case IDC_FORWARD:
308       if (web_contents->GetController().CanGoForward()) {
309         location_bar_->Revert();
310         web_contents->GetController().GoForward();
311       }
312       break;
313     case IDC_STOP:
314       web_contents->Stop();
315       break;
316     case IDC_RELOAD:
317     // Always reload bypassing cache.
318     case IDC_RELOAD_BYPASSING_CACHE:
319     case IDC_RELOAD_CLEARING_CACHE:
320       location_bar_->Revert();
321       web_contents->GetController().Reload(content::ReloadType::BYPASSING_CACHE,
322                                            true);
323       break;
324     default:
325       NOTREACHED();
326   }
327 }
328 
329 std::unique_ptr<views::WidgetDelegate>
MakeWidgetDelegate()330 SimpleWebViewDialog::MakeWidgetDelegate() {
331   auto delegate = std::make_unique<views::WidgetDelegate>();
332   delegate->SetInitiallyFocusedView(web_view_);
333   delegate->SetOwnedByWidget(true);
334   return delegate;
335 }
336 
LoadImages()337 void SimpleWebViewDialog::LoadImages() {
338   const ui::ThemeProvider* tp = GetThemeProvider();
339 
340   back_->SetImage(views::Button::STATE_NORMAL, tp->GetImageSkiaNamed(IDR_BACK));
341   back_->SetImage(views::Button::STATE_HOVERED,
342                   tp->GetImageSkiaNamed(IDR_BACK_H));
343   back_->SetImage(views::Button::STATE_PRESSED,
344                   tp->GetImageSkiaNamed(IDR_BACK_P));
345   back_->SetImage(views::Button::STATE_DISABLED,
346                   tp->GetImageSkiaNamed(IDR_BACK_D));
347 
348   forward_->SetImage(views::Button::STATE_NORMAL,
349                      tp->GetImageSkiaNamed(IDR_FORWARD));
350   forward_->SetImage(views::Button::STATE_HOVERED,
351                      tp->GetImageSkiaNamed(IDR_FORWARD_H));
352   forward_->SetImage(views::Button::STATE_PRESSED,
353                      tp->GetImageSkiaNamed(IDR_FORWARD_P));
354   forward_->SetImage(views::Button::STATE_DISABLED,
355                      tp->GetImageSkiaNamed(IDR_FORWARD_D));
356 }
357 
UpdateButtons()358 void SimpleWebViewDialog::UpdateButtons() {
359   content::NavigationController& navigation_controller =
360       web_view_->web_contents()->GetController();
361   back_->SetEnabled(navigation_controller.CanGoBack());
362   forward_->SetEnabled(navigation_controller.CanGoForward());
363 }
364 
UpdateReload(bool is_loading,bool force)365 void SimpleWebViewDialog::UpdateReload(bool is_loading, bool force) {
366   if (reload_) {
367     reload_->ChangeMode(
368         is_loading ? ReloadButton::Mode::kStop : ReloadButton::Mode::kReload,
369         force);
370   }
371 }
372 
373 }  // namespace chromeos
374