Added #ifdefs around PrintHeaderAndFooter for Windows and Mac.
[chromium-blink-merge.git] / athena / content / web_activity.cc
bloba6bdb82b51a516046566685066eb1c40b0b0422f
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.
5 #include "athena/content/web_activity.h"
7 #include "athena/activity/public/activity_factory.h"
8 #include "athena/activity/public/activity_manager.h"
9 #include "athena/activity/public/activity_view.h"
10 #include "athena/content/content_proxy.h"
11 #include "athena/content/media_utils.h"
12 #include "athena/content/public/dialogs.h"
13 #include "athena/content/web_activity_helpers.h"
14 #include "athena/input/public/accelerator_manager.h"
15 #include "athena/strings/grit/athena_strings.h"
16 #include "base/bind.h"
17 #include "base/command_line.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "components/app_modal_dialogs/javascript_dialog_manager.h"
20 #include "components/favicon_base/select_favicon_frames.h"
21 #include "content/public/browser/native_web_keyboard_event.h"
22 #include "content/public/browser/navigation_controller.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_delegate.h"
25 #include "content/public/common/content_switches.h"
26 #include "content/public/common/favicon_url.h"
27 #include "ui/aura/window.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/compositor/closure_animation_observer.h"
30 #include "ui/compositor/scoped_layer_animation_settings.h"
31 #include "ui/views/background.h"
32 #include "ui/views/controls/label.h"
33 #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
34 #include "ui/views/controls/webview/webview.h"
35 #include "ui/views/focus/focus_manager.h"
36 #include "ui/views/widget/widget.h"
38 namespace athena {
39 namespace {
41 class WebActivityController : public AcceleratorHandler {
42 public:
43 enum Command {
44 CMD_BACK,
45 CMD_FORWARD,
46 CMD_RELOAD,
47 CMD_RELOAD_IGNORE_CACHE,
48 CMD_CLOSE,
49 CMD_STOP,
52 explicit WebActivityController(WebActivity* owner_activity,
53 views::WebView* web_view)
54 : owner_activity_(owner_activity),
55 web_view_(web_view),
56 reserved_accelerator_enabled_(true) {}
57 ~WebActivityController() override {}
59 // Installs accelerators for web activity.
60 void InstallAccelerators() {
61 accelerator_manager_ = AcceleratorManager::CreateForFocusManager(
62 web_view_->GetFocusManager()).Pass();
63 const AcceleratorData accelerator_data[] = {
64 {TRIGGER_ON_PRESS, ui::VKEY_R, ui::EF_CONTROL_DOWN, CMD_RELOAD,
65 AF_NONE},
66 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_NONE, CMD_RELOAD,
67 AF_NONE},
68 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_CONTROL_DOWN,
69 CMD_RELOAD_IGNORE_CACHE, AF_NONE},
70 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_FORWARD, ui::EF_NONE, CMD_FORWARD,
71 AF_NONE},
72 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_BACK, ui::EF_NONE, CMD_BACK,
73 AF_NONE},
74 {TRIGGER_ON_PRESS, ui::VKEY_W, ui::EF_CONTROL_DOWN, CMD_CLOSE, AF_NONE},
75 {TRIGGER_ON_PRESS, ui::VKEY_ESCAPE, ui::EF_NONE, CMD_STOP, AF_NONE},
77 accelerator_manager_->RegisterAccelerators(
78 accelerator_data, arraysize(accelerator_data), this);
81 // Methods that are called before and after key events are consumed by the web
82 // contents.
83 // See the documentation in WebContentsDelegate: for more details.
84 bool PreHandleKeyboardEvent(content::WebContents* source,
85 const content::NativeWebKeyboardEvent& event,
86 bool* is_keyboard_shortcut) {
87 ui::Accelerator accelerator(
88 static_cast<ui::KeyboardCode>(event.windowsKeyCode),
89 content::GetModifiersFromNativeWebKeyboardEvent(event));
90 if (event.type == blink::WebInputEvent::KeyUp)
91 accelerator.set_type(ui::ET_KEY_RELEASED);
93 if (reserved_accelerator_enabled_ &&
94 accelerator_manager_->IsRegistered(accelerator, AF_RESERVED)) {
95 return web_view_->GetFocusManager()->ProcessAccelerator(accelerator);
97 *is_keyboard_shortcut =
98 accelerator_manager_->IsRegistered(accelerator, AF_NONE);
99 return false;
102 void HandleKeyboardEvent(content::WebContents* source,
103 const content::NativeWebKeyboardEvent& event) {
104 unhandled_keyboard_event_handler_.HandleKeyboardEvent(
105 event, web_view_->GetFocusManager());
108 private:
109 // AcceleratorHandler:
110 bool IsCommandEnabled(int command_id) const override {
111 switch (command_id) {
112 case CMD_RELOAD:
113 case CMD_RELOAD_IGNORE_CACHE:
114 return true;
115 case CMD_BACK:
116 return web_view_->GetWebContents()->GetController().CanGoBack();
117 case CMD_FORWARD:
118 return web_view_->GetWebContents()->GetController().CanGoForward();
119 case CMD_CLOSE:
120 // TODO(oshima): check onbeforeunload handler.
121 return true;
122 case CMD_STOP:
123 return web_view_->GetWebContents()->IsLoading();
125 return false;
128 bool OnAcceleratorFired(int command_id,
129 const ui::Accelerator& accelerator) override {
130 switch (command_id) {
131 case CMD_RELOAD:
132 web_view_->GetWebContents()->GetController().Reload(false);
133 return true;
134 case CMD_RELOAD_IGNORE_CACHE:
135 web_view_->GetWebContents()->GetController().ReloadIgnoringCache(false);
136 return true;
137 case CMD_BACK:
138 web_view_->GetWebContents()->GetController().GoBack();
139 return true;
140 case CMD_FORWARD:
141 web_view_->GetWebContents()->GetController().GoForward();
142 return true;
143 case CMD_CLOSE:
144 Activity::Delete(owner_activity_);
145 return true;
146 case CMD_STOP:
147 web_view_->GetWebContents()->Stop();
148 return true;
150 return false;
153 Activity* const owner_activity_;
154 views::WebView* web_view_;
155 bool reserved_accelerator_enabled_;
156 scoped_ptr<AcceleratorManager> accelerator_manager_;
157 views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
159 DISALLOW_COPY_AND_ASSIGN(WebActivityController);
162 const SkColor kDefaultTitleColor = SkColorSetRGB(0xf2, 0xf2, 0xf2);
163 const int kIconSize = 32;
164 const float kDistanceShowReloadMessage = 100;
165 const float kDistanceReload = 150;
167 } // namespace
169 // A web view for athena's web activity. Note that AthenaWebView will create its
170 // own content so that it can eject and reload it.
171 class AthenaWebView : public views::WebView {
172 public:
173 explicit AthenaWebView(content::BrowserContext* context,
174 WebActivity* owner_activity)
175 : views::WebView(context),
176 controller_(new WebActivityController(owner_activity, this)),
177 owner_activity_(owner_activity),
178 fullscreen_(false),
179 overscroll_y_(0) {
180 SetEmbedFullscreenWidgetMode(true);
181 // TODO(skuhne): Add content observer to detect renderer crash and set
182 // content status to unloaded if that happens.
185 AthenaWebView(content::WebContents* web_contents,
186 WebActivity* owner_activity)
187 : views::WebView(web_contents->GetBrowserContext()),
188 controller_(new WebActivityController(owner_activity, this)),
189 owner_activity_(owner_activity) {
190 scoped_ptr<content::WebContents> old_contents(
191 SwapWebContents(scoped_ptr<content::WebContents>(web_contents)));
194 ~AthenaWebView() override {}
196 void InstallAccelerators() { controller_->InstallAccelerators(); }
198 void EvictContent() {
199 scoped_ptr<content::WebContents> old_contents(SwapWebContents(
200 scoped_ptr<content::WebContents>(content::WebContents::Create(
201 content::WebContents::CreateParams(browser_context())))));
202 // If there is a progress bar, we need to get rid of it now since its
203 // associated content, parent window and layers will disappear with evicting
204 // the content.
205 progress_bar_.reset();
206 evicted_web_contents_.reset(
207 content::WebContents::Create(content::WebContents::CreateParams(
208 old_contents->GetBrowserContext())));
209 evicted_web_contents_->GetController().CopyStateFrom(
210 old_contents->GetController());
211 // As soon as the new contents becomes visible, it should reload.
212 // TODO(skuhne): This breaks script connections with other activities.
213 // Even though this is the same technique as used by the TabStripModel,
214 // we might want to address this cleaner since we are more likely to
215 // run into this state. by unloading.
218 void AttachHelpers() {
219 if (!IsContentEvicted())
220 AttachWebActivityHelpers(GetWebContents());
221 // Else: The helpers will be attached when the evicted content is reloaded.
224 void ReloadEvictedContent() {
225 CHECK(evicted_web_contents_.get());
227 // Order is important. The helpers must be attached prior to the RenderView
228 // being created.
229 AttachWebActivityHelpers(evicted_web_contents_.get());
231 SwapWebContents(evicted_web_contents_.Pass());
234 // Check if the content got evicted.
235 const bool IsContentEvicted() { return !!evicted_web_contents_.get(); }
237 // content::WebContentsDelegate:
238 content::WebContents* OpenURLFromTab(
239 content::WebContents* source,
240 const content::OpenURLParams& params) override {
241 switch(params.disposition) {
242 case CURRENT_TAB: {
243 DCHECK(source == web_contents());
244 content::NavigationController::LoadURLParams load_url_params(
245 params.url);
246 load_url_params.referrer = params.referrer;
247 load_url_params.frame_tree_node_id = params.frame_tree_node_id;
248 load_url_params.transition_type = params.transition;
249 load_url_params.extra_headers = params.extra_headers;
250 load_url_params.should_replace_current_entry =
251 params.should_replace_current_entry;
252 load_url_params.is_renderer_initiated = params.is_renderer_initiated;
253 load_url_params.transferred_global_request_id =
254 params.transferred_global_request_id;
255 web_contents()->GetController().LoadURLWithParams(load_url_params);
256 return web_contents();
258 case NEW_FOREGROUND_TAB:
259 case NEW_BACKGROUND_TAB:
260 case NEW_POPUP:
261 case NEW_WINDOW: {
262 Activity* activity = ActivityFactory::Get()->CreateWebActivity(
263 browser_context(), base::string16(), params.url);
264 Activity::Show(activity);
265 break;
267 default:
268 break;
270 // nullptr is returned if the URL wasn't opened immediately.
271 return nullptr;
274 bool CanOverscrollContent() const override {
275 const std::string value = CommandLine::ForCurrentProcess()->
276 GetSwitchValueASCII(switches::kOverscrollHistoryNavigation);
277 return value != "0";
280 void OverscrollUpdate(float delta_y) override {
281 overscroll_y_ = delta_y;
282 if (overscroll_y_ > kDistanceShowReloadMessage) {
283 if (!reload_message_)
284 CreateReloadMessage();
285 reload_message_->Show();
286 float opacity = 1.0f;
287 if (overscroll_y_ < kDistanceReload) {
288 opacity = (overscroll_y_ - kDistanceShowReloadMessage) /
289 (kDistanceReload - kDistanceShowReloadMessage);
291 reload_message_->GetLayer()->SetOpacity(opacity);
292 } else if (reload_message_) {
293 reload_message_->Hide();
297 void OverscrollComplete() override {
298 if (overscroll_y_ >= kDistanceReload)
299 GetWebContents()->GetController().Reload(false);
300 if (reload_message_)
301 reload_message_->Hide();
302 overscroll_y_ = 0;
305 void AddNewContents(content::WebContents* source,
306 content::WebContents* new_contents,
307 WindowOpenDisposition disposition,
308 const gfx::Rect& initial_pos,
309 bool user_gesture,
310 bool* was_blocked) override {
311 Activity* activity =
312 ActivityFactory::Get()->CreateWebActivity(new_contents);
313 Activity::Show(activity);
316 bool PreHandleKeyboardEvent(content::WebContents* source,
317 const content::NativeWebKeyboardEvent& event,
318 bool* is_keyboard_shortcut) override {
319 return controller_->PreHandleKeyboardEvent(
320 source, event, is_keyboard_shortcut);
323 void HandleKeyboardEvent(
324 content::WebContents* source,
325 const content::NativeWebKeyboardEvent& event) override {
326 controller_->HandleKeyboardEvent(source, event);
329 void ToggleFullscreenModeForTab(content::WebContents* web_contents,
330 bool enter_fullscreen) override {
331 fullscreen_ = enter_fullscreen;
332 GetWidget()->SetFullscreen(fullscreen_);
335 bool IsFullscreenForTabOrPending(
336 const content::WebContents* web_contents) const override {
337 return fullscreen_;
340 void LoadingStateChanged(content::WebContents* source,
341 bool to_different_document) override {
342 bool has_stopped = source == nullptr || !source->IsLoading();
343 LoadProgressChanged(source, has_stopped ? 1 : 0);
346 void LoadProgressChanged(content::WebContents* source,
347 double progress) override {
348 if (!progress)
349 return;
351 if (!progress_bar_) {
352 CreateProgressBar();
353 source->GetNativeView()->layer()->Add(progress_bar_.get());
355 progress_bar_->SetBounds(gfx::Rect(
356 0, 0, progress * progress_bar_->parent()->bounds().width(), 3));
357 if (progress < 1)
358 return;
360 ui::ScopedLayerAnimationSettings settings(progress_bar_->GetAnimator());
361 settings.SetTweenType(gfx::Tween::EASE_IN);
362 ui::Layer* layer = progress_bar_.get();
363 settings.AddObserver(new ui::ClosureAnimationObserver(
364 base::Bind(&base::DeletePointer<ui::Layer>, progress_bar_.release())));
365 layer->SetOpacity(0.f);
368 content::JavaScriptDialogManager* GetJavaScriptDialogManager() override {
369 return GetJavaScriptDialogManagerInstance();
372 content::ColorChooser* OpenColorChooser(
373 content::WebContents* web_contents,
374 SkColor color,
375 const std::vector<content::ColorSuggestion>& suggestions) override {
376 return athena::OpenColorChooser(web_contents, color, suggestions);
379 // Called when a file selection is to be done.
380 void RunFileChooser(content::WebContents* web_contents,
381 const content::FileChooserParams& params) override {
382 return athena::OpenFileChooser(web_contents, params);
385 void CloseContents(content::WebContents* contents) override {
386 Activity::Delete(owner_activity_);
389 private:
390 void CreateProgressBar() {
391 CHECK(!progress_bar_);
392 progress_bar_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
393 progress_bar_->SetColor(SkColorSetRGB(0x17, 0x59, 0xcd));
396 void CreateReloadMessage() {
397 CHECK(!reload_message_);
398 reload_message_.reset(new views::Widget);
399 views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
400 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
401 params.parent = GetWidget()->GetNativeView();
402 reload_message_->Init(params);
404 views::Label* label = new views::Label(
405 l10n_util::GetStringUTF16(IDS_ATHENA_PULL_TO_RELOAD_MESSAGE));
406 label->SetBackgroundColor(SK_ColorGRAY);
407 label->set_background(
408 views::Background::CreateSolidBackground(SK_ColorGRAY));
410 reload_message_->SetContentsView(label);
411 reload_message_->SetBounds(ConvertRectToWidget(
412 gfx::Rect(0, 0, width(), label->GetPreferredSize().height())));
415 scoped_ptr<WebActivityController> controller_;
417 Activity* const owner_activity_;
419 // If the activity got evicted, this is the web content which holds the known
420 // state of the content before eviction.
421 scoped_ptr<content::WebContents> evicted_web_contents_;
423 scoped_ptr<ui::Layer> progress_bar_;
425 scoped_ptr<views::Widget> reload_message_;
427 // TODO(oshima): Find out if we should support window fullscreen.
428 // It may still useful when a user is in split mode.
429 bool fullscreen_;
431 // The distance that the user has overscrolled vertically.
432 float overscroll_y_;
434 DISALLOW_COPY_AND_ASSIGN(AthenaWebView);
437 WebActivity::WebActivity(content::BrowserContext* browser_context,
438 const base::string16& title,
439 const GURL& url)
440 : browser_context_(browser_context),
441 web_view_(new AthenaWebView(browser_context, this)),
442 title_(title),
443 title_color_(kDefaultTitleColor),
444 current_state_(ACTIVITY_UNLOADED),
445 activity_view_(nullptr),
446 weak_ptr_factory_(this) {
447 // Order is important. The web activity helpers must be attached prior to the
448 // RenderView being created.
449 SetCurrentState(ACTIVITY_INVISIBLE);
450 web_view_->LoadInitialURL(url);
453 WebActivity::WebActivity(content::WebContents* contents)
454 : browser_context_(contents->GetBrowserContext()),
455 web_view_(new AthenaWebView(contents, this)),
456 title_color_(kDefaultTitleColor),
457 current_state_(ACTIVITY_UNLOADED),
458 activity_view_(nullptr),
459 weak_ptr_factory_(this) {
460 // If the activity was created as a result of
461 // WebContentsDelegate::AddNewContents(), web activity helpers may not be
462 // created prior to the RenderView being created. Desktop Chrome has a
463 // similar problem.
464 SetCurrentState(ACTIVITY_INVISIBLE);
467 WebActivity::~WebActivity() {
468 // It is not required to change the activity state to UNLOADED - unless we
469 // would add state observers.
472 ActivityViewModel* WebActivity::GetActivityViewModel() {
473 return this;
476 void WebActivity::SetCurrentState(Activity::ActivityState state) {
477 DCHECK_NE(state, current_state_);
478 if (current_state_ == ACTIVITY_UNLOADED) {
479 web_view_->AttachHelpers();
480 if (web_view_->IsContentEvicted())
481 web_view_->ReloadEvictedContent();
482 Observe(web_view_->GetWebContents());
485 switch (state) {
486 case ACTIVITY_VISIBLE:
487 HideContentProxy();
488 break;
489 case ACTIVITY_INVISIBLE:
490 if (current_state_ == ACTIVITY_VISIBLE)
491 ShowContentProxy();
493 break;
494 case ACTIVITY_BACKGROUND_LOW_PRIORITY:
495 DCHECK(ACTIVITY_VISIBLE == current_state_ ||
496 ACTIVITY_INVISIBLE == current_state_);
497 // TODO(skuhne): Do this.
498 break;
499 case ACTIVITY_PERSISTENT:
500 DCHECK_EQ(ACTIVITY_BACKGROUND_LOW_PRIORITY, current_state_);
501 // TODO(skuhne): Do this. As soon as the new resource management is
502 // agreed upon - or remove otherwise.
503 break;
504 case ACTIVITY_UNLOADED:
505 DCHECK_NE(ACTIVITY_UNLOADED, current_state_);
506 if (content_proxy_)
507 content_proxy_->ContentWillUnload();
508 Observe(nullptr);
509 web_view_->EvictContent();
510 break;
512 // Remember the last requested state.
513 current_state_ = state;
516 Activity::ActivityState WebActivity::GetCurrentState() {
517 // If the content is evicted, the state has to be UNLOADED.
518 DCHECK(!web_view_->IsContentEvicted() ||
519 current_state_ == ACTIVITY_UNLOADED);
520 return current_state_;
523 bool WebActivity::IsVisible() {
524 return web_view_->visible() && current_state_ != ACTIVITY_UNLOADED;
527 Activity::ActivityMediaState WebActivity::GetMediaState() {
528 return current_state_ == ACTIVITY_UNLOADED ?
529 Activity::ACTIVITY_MEDIA_STATE_NONE :
530 GetActivityMediaState(GetWebContents());
533 aura::Window* WebActivity::GetWindow() {
534 return web_view_->GetWidget() ? web_view_->GetWidget()->GetNativeWindow()
535 : nullptr;
538 content::WebContents* WebActivity::GetWebContents() {
539 return web_view_->GetWebContents();
542 void WebActivity::Init() {
543 web_view_->InstallAccelerators();
546 SkColor WebActivity::GetRepresentativeColor() const {
547 return title_color_;
550 base::string16 WebActivity::GetTitle() const {
551 if (!title_.empty())
552 return title_;
553 const base::string16& title = web_view_->GetWebContents()->GetTitle();
554 if (!title.empty())
555 return title;
556 return base::UTF8ToUTF16(web_view_->GetWebContents()->GetVisibleURL().host());
559 gfx::ImageSkia WebActivity::GetIcon() const {
560 return icon_;
563 void WebActivity::SetActivityView(ActivityView* view) {
564 DCHECK(!activity_view_);
565 activity_view_ = view;
568 bool WebActivity::UsesFrame() const {
569 return true;
572 views::View* WebActivity::GetContentsView() {
573 return web_view_;
576 gfx::ImageSkia WebActivity::GetOverviewModeImage() {
577 if (content_proxy_.get())
578 return content_proxy_->GetContentImage();
579 return gfx::ImageSkia();
582 void WebActivity::PrepareContentsForOverview() {
583 // Turn on fast resizing to avoid re-laying out the web contents when
584 // entering / exiting overview mode and the content is visible.
585 if (!content_proxy_.get())
586 web_view_->SetFastResize(true);
589 void WebActivity::ResetContentsView() {
590 // Turn on fast resizing to avoid re-laying out the web contents when
591 // entering / exiting overview mode and the content is visible.
592 if (!content_proxy_.get()) {
593 web_view_->SetFastResize(false);
594 web_view_->Layout();
598 void WebActivity::TitleWasSet(content::NavigationEntry* entry,
599 bool explicit_set) {
600 if (activity_view_)
601 activity_view_->UpdateTitle();
604 void WebActivity::DidNavigateMainFrame(
605 const content::LoadCommittedDetails& details,
606 const content::FrameNavigateParams& params) {
607 // Prevent old image requests from calling back to OnDidDownloadFavicon().
608 weak_ptr_factory_.InvalidateWeakPtrs();
610 icon_ = gfx::ImageSkia();
611 if (activity_view_)
612 activity_view_->UpdateIcon();
615 void WebActivity::DidUpdateFaviconURL(
616 const std::vector<content::FaviconURL>& candidates) {
617 // Pick an arbitrary favicon of type FAVICON to use.
618 // TODO(pkotwicz): Do something better once the favicon code is componentized.
619 // (crbug.com/401997)
620 weak_ptr_factory_.InvalidateWeakPtrs();
621 for (size_t i = 0; i < candidates.size(); ++i) {
622 if (candidates[i].icon_type == content::FaviconURL::FAVICON) {
623 web_view_->GetWebContents()->DownloadImage(
624 candidates[i].icon_url,
625 true,
627 base::Bind(&WebActivity::OnDidDownloadFavicon,
628 weak_ptr_factory_.GetWeakPtr()));
629 break;
634 void WebActivity::OnDidDownloadFavicon(
635 int id,
636 int http_status_code,
637 const GURL& url,
638 const std::vector<SkBitmap>& bitmaps,
639 const std::vector<gfx::Size>& original_bitmap_sizes) {
640 icon_ = CreateFaviconImageSkia(
641 bitmaps, original_bitmap_sizes, kIconSize, nullptr);
642 if (activity_view_)
643 activity_view_->UpdateIcon();
646 void WebActivity::DidChangeThemeColor(SkColor theme_color) {
647 title_color_ = theme_color;
648 if (activity_view_)
649 activity_view_->UpdateRepresentativeColor();
652 void WebActivity::HideContentProxy() {
653 if (content_proxy_.get())
654 content_proxy_.reset(nullptr);
657 void WebActivity::ShowContentProxy() {
658 if (!content_proxy_.get())
659 content_proxy_.reset(new ContentProxy(web_view_));
662 } // namespace athena