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/input/public/accelerator_manager.h"
10 #include "base/bind.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "content/public/browser/native_web_keyboard_event.h"
13 #include "content/public/browser/navigation_controller.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/browser/web_contents_delegate.h"
16 #include "ui/aura/window.h"
17 #include "ui/compositor/closure_animation_observer.h"
18 #include "ui/compositor/scoped_layer_animation_settings.h"
19 #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
20 #include "ui/views/controls/webview/webview.h"
21 #include "ui/views/focus/focus_manager.h"
22 #include "ui/views/widget/widget.h"
27 class WebActivityController
: public AcceleratorHandler
{
33 CMD_RELOAD_IGNORE_CACHE
,
38 explicit WebActivityController(views::WebView
* web_view
)
39 : web_view_(web_view
), reserved_accelerator_enabled_(true) {}
40 virtual ~WebActivityController() {}
42 // Installs accelerators for web activity.
43 void InstallAccelerators() {
44 accelerator_manager_
= AcceleratorManager::CreateForFocusManager(
45 web_view_
->GetFocusManager()).Pass();
46 const AcceleratorData accelerator_data
[] = {
47 {TRIGGER_ON_PRESS
, ui::VKEY_R
, ui::EF_CONTROL_DOWN
, CMD_RELOAD
,
49 {TRIGGER_ON_PRESS
, ui::VKEY_BROWSER_REFRESH
, ui::EF_NONE
, CMD_RELOAD
,
51 {TRIGGER_ON_PRESS
, ui::VKEY_BROWSER_REFRESH
, ui::EF_CONTROL_DOWN
,
52 CMD_RELOAD_IGNORE_CACHE
, AF_NONE
},
53 {TRIGGER_ON_PRESS
, ui::VKEY_BROWSER_FORWARD
, ui::EF_NONE
, CMD_FORWARD
,
55 {TRIGGER_ON_PRESS
, ui::VKEY_BROWSER_BACK
, ui::EF_NONE
, CMD_BACK
,
57 {TRIGGER_ON_PRESS
, ui::VKEY_W
, ui::EF_CONTROL_DOWN
, CMD_CLOSE
, AF_NONE
},
58 {TRIGGER_ON_PRESS
, ui::VKEY_ESCAPE
, ui::EF_NONE
, CMD_STOP
, AF_NONE
},
60 accelerator_manager_
->RegisterAccelerators(
61 accelerator_data
, arraysize(accelerator_data
), this);
64 // Methods that are called before and after key events are consumed by the web
66 // See the documentation in WebContentsDelegate: for more details.
67 bool PreHandleKeyboardEvent(content::WebContents
* source
,
68 const content::NativeWebKeyboardEvent
& event
,
69 bool* is_keyboard_shortcut
) {
70 ui::Accelerator
accelerator(
71 static_cast<ui::KeyboardCode
>(event
.windowsKeyCode
),
72 content::GetModifiersFromNativeWebKeyboardEvent(event
));
73 if (event
.type
== blink::WebInputEvent::KeyUp
)
74 accelerator
.set_type(ui::ET_KEY_RELEASED
);
76 if (reserved_accelerator_enabled_
&&
77 accelerator_manager_
->IsRegistered(accelerator
, AF_RESERVED
)) {
78 return web_view_
->GetFocusManager()->ProcessAccelerator(accelerator
);
80 *is_keyboard_shortcut
=
81 accelerator_manager_
->IsRegistered(accelerator
, AF_NONE
);
85 void HandleKeyboardEvent(content::WebContents
* source
,
86 const content::NativeWebKeyboardEvent
& event
) {
87 unhandled_keyboard_event_handler_
.HandleKeyboardEvent(
88 event
, web_view_
->GetFocusManager());
92 // AcceleratorHandler:
93 virtual bool IsCommandEnabled(int command_id
) const OVERRIDE
{
96 case CMD_RELOAD_IGNORE_CACHE
:
99 return web_view_
->GetWebContents()->GetController().CanGoBack();
101 return web_view_
->GetWebContents()->GetController().CanGoForward();
103 // TODO(oshima): check onbeforeunload handler.
106 return web_view_
->GetWebContents()->IsLoading();
111 virtual bool OnAcceleratorFired(int command_id
,
112 const ui::Accelerator
& accelerator
) OVERRIDE
{
113 switch (command_id
) {
115 web_view_
->GetWebContents()->GetController().Reload(false);
117 case CMD_RELOAD_IGNORE_CACHE
:
118 web_view_
->GetWebContents()->GetController().ReloadIgnoringCache(false);
121 web_view_
->GetWebContents()->GetController().GoBack();
124 web_view_
->GetWebContents()->GetController().GoForward();
127 web_view_
->GetWidget()->Close();
130 web_view_
->GetWebContents()->Stop();
136 views::WebView
* web_view_
;
137 bool reserved_accelerator_enabled_
;
138 scoped_ptr
<AcceleratorManager
> accelerator_manager_
;
139 views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_
;
141 DISALLOW_COPY_AND_ASSIGN(WebActivityController
);
144 const SkColor kDefaultTitleColor
= SkColorSetRGB(0xf2, 0xf2, 0xf2);
145 const SkColor kDefaultUnavailableColor
= SkColorSetRGB(0xbb, 0x77, 0x77);
149 // A web view for athena's web activity. Note that AthenaWebView will create its
150 // own content so that it can eject and reload it.
151 class AthenaWebView
: public views::WebView
{
153 AthenaWebView(content::BrowserContext
* context
)
154 : views::WebView(context
), controller_(new WebActivityController(this)),
156 SetEmbedFullscreenWidgetMode(true);
157 // TODO(skuhne): Add content observer to detect renderer crash and set
158 // content status to unloaded if that happens.
161 AthenaWebView(content::WebContents
* web_contents
)
162 : views::WebView(web_contents
->GetBrowserContext()),
163 controller_(new WebActivityController(this)) {
164 scoped_ptr
<content::WebContents
> old_contents(
165 SwapWebContents(scoped_ptr
<content::WebContents
>(web_contents
)));
168 virtual ~AthenaWebView() {}
170 void InstallAccelerators() { controller_
->InstallAccelerators(); }
172 void EvictContent() {
173 scoped_ptr
<content::WebContents
> old_contents(SwapWebContents(
174 scoped_ptr
<content::WebContents
>(content::WebContents::Create(
175 content::WebContents::CreateParams(browser_context())))));
176 evicted_web_contents_
.reset(
177 content::WebContents::Create(content::WebContents::CreateParams(
178 old_contents
->GetBrowserContext())));
179 evicted_web_contents_
->GetController().CopyStateFrom(
180 old_contents
->GetController());
181 // As soon as the new contents becomes visible, it should reload.
182 // TODO(skuhne): This breaks script connections with other activities.
183 // Even though this is the same technique as used by the TabStripModel,
184 // we might want to address this cleaner since we are more likely to
185 // run into this state. by unloading.
188 void ReloadContent() {
189 CHECK(evicted_web_contents_
.get());
190 scoped_ptr
<content::WebContents
> replaced_contents(SwapWebContents(
191 evicted_web_contents_
.Pass()));
194 // Check if the content got evicted.
195 const bool IsContentEvicted() { return !!evicted_web_contents_
.get(); }
197 // content::WebContentsDelegate:
198 virtual content::WebContents
* OpenURLFromTab(
199 content::WebContents
* source
,
200 const content::OpenURLParams
& params
) OVERRIDE
{
201 switch(params
.disposition
) {
203 DCHECK(source
== web_contents());
204 content::NavigationController::LoadURLParams
load_url_params(
206 load_url_params
.referrer
= params
.referrer
;
207 load_url_params
.frame_tree_node_id
= params
.frame_tree_node_id
;
208 load_url_params
.transition_type
= params
.transition
;
209 load_url_params
.extra_headers
= params
.extra_headers
;
210 load_url_params
.should_replace_current_entry
=
211 params
.should_replace_current_entry
;
212 load_url_params
.is_renderer_initiated
= params
.is_renderer_initiated
;
213 load_url_params
.transferred_global_request_id
=
214 params
.transferred_global_request_id
;
215 web_contents()->GetController().LoadURLWithParams(load_url_params
);
216 return web_contents();
218 case NEW_FOREGROUND_TAB
:
219 case NEW_BACKGROUND_TAB
:
222 ActivityManager::Get()->AddActivity(
223 ActivityFactory::Get()->CreateWebActivity(browser_context(),
230 // NULL is returned if the URL wasn't opened immediately.
234 virtual void AddNewContents(content::WebContents
* source
,
235 content::WebContents
* new_contents
,
236 WindowOpenDisposition disposition
,
237 const gfx::Rect
& initial_pos
,
239 bool* was_blocked
) OVERRIDE
{
240 ActivityManager::Get()->AddActivity(
241 new WebActivity(new AthenaWebView(new_contents
)));
244 virtual bool PreHandleKeyboardEvent(
245 content::WebContents
* source
,
246 const content::NativeWebKeyboardEvent
& event
,
247 bool* is_keyboard_shortcut
) OVERRIDE
{
248 return controller_
->PreHandleKeyboardEvent(
249 source
, event
, is_keyboard_shortcut
);
252 virtual void HandleKeyboardEvent(
253 content::WebContents
* source
,
254 const content::NativeWebKeyboardEvent
& event
) OVERRIDE
{
255 controller_
->HandleKeyboardEvent(source
, event
);
258 virtual void ToggleFullscreenModeForTab(content::WebContents
* web_contents
,
259 bool enter_fullscreen
) OVERRIDE
{
260 fullscreen_
= enter_fullscreen
;
261 GetWidget()->SetFullscreen(fullscreen_
);
264 virtual bool IsFullscreenForTabOrPending(
265 const content::WebContents
* web_contents
) const OVERRIDE
{
269 virtual void LoadingStateChanged(content::WebContents
* source
,
270 bool to_different_document
) OVERRIDE
{
271 bool has_stopped
= source
== NULL
|| !source
->IsLoading();
272 LoadProgressChanged(source
, has_stopped
? 1 : 0);
275 virtual void LoadProgressChanged(content::WebContents
* source
,
276 double progress
) OVERRIDE
{
280 if (!progress_bar_
) {
282 source
->GetNativeView()->layer()->Add(progress_bar_
.get());
284 progress_bar_
->SetBounds(gfx::Rect(
285 0, 0, progress
* progress_bar_
->parent()->bounds().width(), 3));
289 ui::ScopedLayerAnimationSettings
settings(progress_bar_
->GetAnimator());
290 settings
.SetTweenType(gfx::Tween::EASE_IN
);
291 ui::Layer
* layer
= progress_bar_
.get();
292 settings
.AddObserver(new ui::ClosureAnimationObserver(
293 base::Bind(&base::DeletePointer
<ui::Layer
>, progress_bar_
.release())));
294 layer
->SetOpacity(0.f
);
298 void CreateProgressBar() {
299 CHECK(!progress_bar_
);
300 progress_bar_
.reset(new ui::Layer(ui::LAYER_SOLID_COLOR
));
301 progress_bar_
->SetColor(SkColorSetRGB(0x00, 0xb0, 0xc7));
304 scoped_ptr
<WebActivityController
> controller_
;
306 // If the activity got evicted, this is the web content which holds the known
307 // state of the content before eviction.
308 scoped_ptr
<content::WebContents
> evicted_web_contents_
;
310 scoped_ptr
<ui::Layer
> progress_bar_
;
312 // TODO(oshima): Find out if we should support window fullscreen.
313 // It may still useful when a user is in split mode.
316 DISALLOW_COPY_AND_ASSIGN(AthenaWebView
);
319 WebActivity::WebActivity(content::BrowserContext
* browser_context
,
321 : browser_context_(browser_context
),
324 title_color_(kDefaultTitleColor
),
325 current_state_(ACTIVITY_UNLOADED
) {
328 WebActivity::WebActivity(AthenaWebView
* web_view
)
329 : browser_context_(web_view
->browser_context()),
330 url_(web_view
->GetWebContents()->GetURL()),
332 current_state_(ACTIVITY_UNLOADED
) {
333 // Transition to state ACTIVITY_INVISIBLE to perform the same setup steps
334 // as on new activities (namely adding a WebContentsObserver).
335 SetCurrentState(ACTIVITY_INVISIBLE
);
338 WebActivity::~WebActivity() {
339 // It is not required to change the activity state to UNLOADED - unless we
340 // would add state observers.
343 ActivityViewModel
* WebActivity::GetActivityViewModel() {
347 void WebActivity::SetCurrentState(Activity::ActivityState state
) {
349 case ACTIVITY_VISIBLE
:
350 // Fall through (for the moment).
351 case ACTIVITY_INVISIBLE
:
352 // By clearing the overview mode image we allow the content to be shown.
353 overview_mode_image_
= gfx::ImageSkia();
354 if (web_view_
->IsContentEvicted()) {
355 DCHECK_EQ(ACTIVITY_UNLOADED
, current_state_
);
356 web_view_
->ReloadContent();
358 Observe(web_view_
->GetWebContents());
360 case ACTIVITY_BACKGROUND_LOW_PRIORITY
:
361 DCHECK(ACTIVITY_VISIBLE
== current_state_
||
362 ACTIVITY_INVISIBLE
== current_state_
);
363 // TODO(skuhne): Do this.
365 case ACTIVITY_PERSISTENT
:
366 DCHECK_EQ(ACTIVITY_BACKGROUND_LOW_PRIORITY
, current_state_
);
367 // TODO(skuhne): Do this. As soon as the new resource management is
368 // agreed upon - or remove otherwise.
370 case ACTIVITY_UNLOADED
:
371 DCHECK_NE(ACTIVITY_UNLOADED
, current_state_
);
373 web_view_
->EvictContent();
376 // Remember the last requested state.
377 current_state_
= state
;
380 Activity::ActivityState
WebActivity::GetCurrentState() {
381 if (!web_view_
|| web_view_
->IsContentEvicted()) {
382 DCHECK_EQ(ACTIVITY_UNLOADED
, current_state_
);
383 return ACTIVITY_UNLOADED
;
385 // TODO(skuhne): This should be controlled by an observer and should not
387 if (IsVisible() && current_state_
!= ACTIVITY_VISIBLE
)
388 SetCurrentState(ACTIVITY_VISIBLE
);
389 // Note: If the activity is not visible it does not necessarily mean that it
390 // does not have GPU compositor resources (yet).
392 return current_state_
;
395 bool WebActivity::IsVisible() {
397 web_view_
->IsDrawn() &&
398 current_state_
!= ACTIVITY_UNLOADED
&&
400 GetWindow()->IsVisible();
403 Activity::ActivityMediaState
WebActivity::GetMediaState() {
404 // TODO(skuhne): The function GetTabMediaStateForContents(WebContents),
405 // and the AudioStreamMonitor needs to be moved from Chrome into contents to
406 // make it more modular and so that we can use it from here.
407 return Activity::ACTIVITY_MEDIA_STATE_NONE
;
410 aura::Window
* WebActivity::GetWindow() {
411 return !web_view_
? NULL
: web_view_
->GetWidget()->GetNativeWindow();
414 void WebActivity::Init() {
416 web_view_
->InstallAccelerators();
419 SkColor
WebActivity::GetRepresentativeColor() const {
420 // TODO(sad): Compute the color from the favicon.
421 return web_view_
? title_color_
: kDefaultUnavailableColor
;
424 base::string16
WebActivity::GetTitle() const {
425 return web_view_
? base::UTF8ToUTF16(
426 web_view_
->GetWebContents()->GetVisibleURL().host())
430 bool WebActivity::UsesFrame() const {
434 views::View
* WebActivity::GetContentsView() {
436 web_view_
= new AthenaWebView(browser_context_
);
437 web_view_
->LoadInitialURL(url_
);
438 SetCurrentState(ACTIVITY_INVISIBLE
);
439 // Reset the overview mode image.
440 overview_mode_image_
= gfx::ImageSkia();
445 void WebActivity::CreateOverviewModeImage() {
446 // TODO(skuhne): Create an overview.
449 gfx::ImageSkia
WebActivity::GetOverviewModeImage() {
450 return overview_mode_image_
;
453 void WebActivity::TitleWasSet(content::NavigationEntry
* entry
,
455 ActivityManager::Get()->UpdateActivity(this);
458 void WebActivity::DidUpdateFaviconURL(
459 const std::vector
<content::FaviconURL
>& candidates
) {
460 ActivityManager::Get()->UpdateActivity(this);
463 void WebActivity::DidChangeThemeColor(SkColor theme_color
) {
464 title_color_
= theme_color
;
467 } // namespace athena