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/strings/utf_string_conversions.h"
11 #include "content/public/browser/native_web_keyboard_event.h"
12 #include "content/public/browser/navigation_controller.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/browser/web_contents_delegate.h"
15 #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
16 #include "ui/views/controls/webview/webview.h"
17 #include "ui/views/focus/focus_manager.h"
18 #include "ui/views/widget/widget.h"
23 class WebActivityController : public AcceleratorHandler {
29 CMD_RELOAD_IGNORE_CACHE,
32 explicit WebActivityController(views::WebView* web_view)
33 : web_view_(web_view), reserved_accelerator_enabled_(true) {}
34 virtual ~WebActivityController() {}
36 // Installs accelerators for web activity.
37 void InstallAccelerators() {
38 accelerator_manager_ = AcceleratorManager::CreateForFocusManager(
39 web_view_->GetFocusManager()).Pass();
40 const AcceleratorData accelerator_data[] = {
41 {TRIGGER_ON_PRESS, ui::VKEY_R, ui::EF_CONTROL_DOWN, CMD_RELOAD,
43 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_NONE, CMD_RELOAD,
45 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_CONTROL_DOWN,
46 CMD_RELOAD_IGNORE_CACHE, AF_NONE},
47 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_FORWARD, ui::EF_NONE, CMD_FORWARD,
49 {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_BACK, ui::EF_NONE, CMD_BACK,
52 accelerator_manager_->RegisterAccelerators(
53 accelerator_data, arraysize(accelerator_data), this);
56 // Methods that are called before and after key events are consumed by the web
58 // See the documentation in WebContentsDelegate: for more details.
59 bool PreHandleKeyboardEvent(content::WebContents* source,
60 const content::NativeWebKeyboardEvent& event,
61 bool* is_keyboard_shortcut) {
62 ui::Accelerator accelerator(
63 static_cast<ui::KeyboardCode>(event.windowsKeyCode),
64 content::GetModifiersFromNativeWebKeyboardEvent(event));
65 if (event.type == blink::WebInputEvent::KeyUp)
66 accelerator.set_type(ui::ET_KEY_RELEASED);
68 if (reserved_accelerator_enabled_ &&
69 accelerator_manager_->IsRegistered(accelerator, AF_RESERVED)) {
70 return web_view_->GetFocusManager()->ProcessAccelerator(accelerator);
72 *is_keyboard_shortcut =
73 accelerator_manager_->IsRegistered(accelerator, AF_NONE);
77 void HandleKeyboardEvent(content::WebContents* source,
78 const content::NativeWebKeyboardEvent& event) {
79 unhandled_keyboard_event_handler_.HandleKeyboardEvent(
80 event, web_view_->GetFocusManager());
84 // AcceleratorHandler:
85 virtual bool IsCommandEnabled(int command_id) const OVERRIDE {
90 return web_view_->GetWebContents()->GetController().CanGoBack();
92 return web_view_->GetWebContents()->GetController().CanGoForward();
97 virtual bool OnAcceleratorFired(int command_id,
98 const ui::Accelerator& accelerator) OVERRIDE {
101 web_view_->GetWebContents()->GetController().Reload(false);
103 case CMD_RELOAD_IGNORE_CACHE:
104 web_view_->GetWebContents()->GetController().ReloadIgnoringCache(false);
107 web_view_->GetWebContents()->GetController().GoBack();
110 web_view_->GetWebContents()->GetController().GoForward();
116 views::WebView* web_view_;
117 bool reserved_accelerator_enabled_;
118 scoped_ptr<AcceleratorManager> accelerator_manager_;
119 views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
121 DISALLOW_COPY_AND_ASSIGN(WebActivityController);
124 const SkColor kDefaultTitleColor = SkColorSetRGB(0xf2, 0xf2, 0xf2);
125 const SkColor kDefaultUnavailableColor = SkColorSetRGB(0xbb, 0x77, 0x77);
129 // A web view for athena's web activity. Note that AthenaWebView will create its
130 // own content so that it can eject and reload it.
131 class AthenaWebView : public views::WebView {
133 AthenaWebView(content::BrowserContext* context)
134 : views::WebView(context), controller_(new WebActivityController(this)),
136 SetEmbedFullscreenWidgetMode(true);
137 // TODO(skuhne): Add content observer to detect renderer crash and set
138 // content status to unloaded if that happens.
141 AthenaWebView(content::WebContents* web_contents)
142 : views::WebView(web_contents->GetBrowserContext()),
143 controller_(new WebActivityController(this)) {
144 scoped_ptr<content::WebContents> old_contents(
145 SwapWebContents(scoped_ptr<content::WebContents>(web_contents)));
148 virtual ~AthenaWebView() {}
150 void InstallAccelerators() { controller_->InstallAccelerators(); }
152 void EvictContent() {
153 scoped_ptr<content::WebContents> old_contents(SwapWebContents(
154 scoped_ptr<content::WebContents>(content::WebContents::Create(
155 content::WebContents::CreateParams(browser_context())))));
156 evicted_web_contents_.reset(
157 content::WebContents::Create(content::WebContents::CreateParams(
158 old_contents->GetBrowserContext())));
159 evicted_web_contents_->GetController().CopyStateFrom(
160 old_contents->GetController());
161 // As soon as the new contents becomes visible, it should reload.
162 // TODO(skuhne): This breaks script connections with other activities.
163 // Even though this is the same technique as used by the TabStripModel,
164 // we might want to address this cleaner since we are more likely to
165 // run into this state. by unloading.
168 void ReloadContent() {
169 CHECK(evicted_web_contents_.get());
170 scoped_ptr<content::WebContents> replaced_contents(SwapWebContents(
171 evicted_web_contents_.Pass()));
174 // Check if the content got evicted.
175 const bool IsContentEvicted() { return !!evicted_web_contents_.get(); }
177 // content::WebContentsDelegate:
178 virtual content::WebContents* OpenURLFromTab(
179 content::WebContents* source,
180 const content::OpenURLParams& params) OVERRIDE {
181 switch(params.disposition) {
183 DCHECK(source == web_contents());
184 content::NavigationController::LoadURLParams load_url_params(
186 load_url_params.referrer = params.referrer;
187 load_url_params.frame_tree_node_id = params.frame_tree_node_id;
188 load_url_params.transition_type = params.transition;
189 load_url_params.extra_headers = params.extra_headers;
190 load_url_params.should_replace_current_entry =
191 params.should_replace_current_entry;
192 load_url_params.is_renderer_initiated = params.is_renderer_initiated;
193 load_url_params.transferred_global_request_id =
194 params.transferred_global_request_id;
195 web_contents()->GetController().LoadURLWithParams(load_url_params);
196 return web_contents();
198 case NEW_FOREGROUND_TAB:
199 case NEW_BACKGROUND_TAB:
202 ActivityManager::Get()->AddActivity(
203 ActivityFactory::Get()->CreateWebActivity(browser_context(),
210 // NULL is returned if the URL wasn't opened immediately.
214 virtual void AddNewContents(content::WebContents* source,
215 content::WebContents* new_contents,
216 WindowOpenDisposition disposition,
217 const gfx::Rect& initial_pos,
219 bool* was_blocked) OVERRIDE {
220 ActivityManager::Get()->AddActivity(
221 new WebActivity(new AthenaWebView(new_contents)));
224 virtual bool PreHandleKeyboardEvent(
225 content::WebContents* source,
226 const content::NativeWebKeyboardEvent& event,
227 bool* is_keyboard_shortcut) OVERRIDE {
228 return controller_->PreHandleKeyboardEvent(
229 source, event, is_keyboard_shortcut);
232 virtual void HandleKeyboardEvent(
233 content::WebContents* source,
234 const content::NativeWebKeyboardEvent& event) OVERRIDE {
235 controller_->HandleKeyboardEvent(source, event);
238 virtual void ToggleFullscreenModeForTab(content::WebContents* web_contents,
239 bool enter_fullscreen) OVERRIDE {
240 fullscreen_ = enter_fullscreen;
241 GetWidget()->SetFullscreen(fullscreen_);
244 virtual bool IsFullscreenForTabOrPending(
245 const content::WebContents* web_contents) const OVERRIDE {
250 scoped_ptr<WebActivityController> controller_;
252 // If the activity got evicted, this is the web content which holds the known
253 // state of the content before eviction.
254 scoped_ptr<content::WebContents> evicted_web_contents_;
256 // TODO(oshima): Find out if we should support window fullscreen.
257 // It may still useful when a user is in split mode.
260 DISALLOW_COPY_AND_ASSIGN(AthenaWebView);
263 WebActivity::WebActivity(content::BrowserContext* browser_context,
265 : browser_context_(browser_context),
268 title_color_(kDefaultTitleColor),
269 current_state_(ACTIVITY_UNLOADED) {
272 WebActivity::WebActivity(AthenaWebView* web_view)
273 : browser_context_(web_view->browser_context()),
274 url_(web_view->GetWebContents()->GetURL()),
276 current_state_(ACTIVITY_UNLOADED) {
277 // Transition to state ACTIVITY_INVISIBLE to perform the same setup steps
278 // as on new activities (namely adding a WebContentsObserver).
279 SetCurrentState(ACTIVITY_INVISIBLE);
282 WebActivity::~WebActivity() {
283 // It is not required to change the activity state to UNLOADED - unless we
284 // would add state observers.
287 ActivityViewModel* WebActivity::GetActivityViewModel() {
291 void WebActivity::SetCurrentState(Activity::ActivityState state) {
293 case ACTIVITY_VISIBLE:
294 // Fall through (for the moment).
295 case ACTIVITY_INVISIBLE:
296 // By clearing the overview mode image we allow the content to be shown.
297 overview_mode_image_ = gfx::ImageSkia();
298 if (web_view_->IsContentEvicted()) {
299 DCHECK_EQ(ACTIVITY_UNLOADED, current_state_);
300 web_view_->ReloadContent();
302 Observe(web_view_->GetWebContents());
304 case ACTIVITY_BACKGROUND_LOW_PRIORITY:
305 DCHECK(ACTIVITY_VISIBLE == current_state_ ||
306 ACTIVITY_INVISIBLE == current_state_);
307 // TODO(skuhne): Do this.
309 case ACTIVITY_PERSISTENT:
310 DCHECK_EQ(ACTIVITY_BACKGROUND_LOW_PRIORITY, current_state_);
311 // TODO(skuhne): Do this. As soon as the new resource management is
312 // agreed upon - or remove otherwise.
314 case ACTIVITY_UNLOADED:
315 DCHECK_NE(ACTIVITY_UNLOADED, current_state_);
317 web_view_->EvictContent();
320 // Remember the last requested state.
321 current_state_ = state;
324 Activity::ActivityState WebActivity::GetCurrentState() {
325 if (!web_view_ || web_view_->IsContentEvicted()) {
326 DCHECK_EQ(ACTIVITY_UNLOADED, current_state_);
327 return ACTIVITY_UNLOADED;
329 // TODO(skuhne): This should be controlled by an observer and should not
331 if (IsVisible() && current_state_ != ACTIVITY_VISIBLE)
332 SetCurrentState(ACTIVITY_VISIBLE);
333 // Note: If the activity is not visible it does not necessarily mean that it
334 // does not have GPU compositor resources (yet).
336 return current_state_;
339 bool WebActivity::IsVisible() {
340 return web_view_ && web_view_->IsDrawn();
343 Activity::ActivityMediaState WebActivity::GetMediaState() {
344 // TODO(skuhne): The function GetTabMediaStateForContents(WebContents),
345 // and the AudioStreamMonitor needs to be moved from Chrome into contents to
346 // make it more modular and so that we can use it from here.
347 return Activity::ACTIVITY_MEDIA_STATE_NONE;
350 void WebActivity::Init() {
352 web_view_->InstallAccelerators();
355 SkColor WebActivity::GetRepresentativeColor() const {
356 // TODO(sad): Compute the color from the favicon.
357 return web_view_ ? title_color_ : kDefaultUnavailableColor;
360 base::string16 WebActivity::GetTitle() const {
361 return web_view_ ? base::UTF8ToUTF16(
362 web_view_->GetWebContents()->GetVisibleURL().host())
366 bool WebActivity::UsesFrame() const {
370 views::View* WebActivity::GetContentsView() {
372 web_view_ = new AthenaWebView(browser_context_);
373 web_view_->LoadInitialURL(url_);
374 SetCurrentState(ACTIVITY_INVISIBLE);
375 // Reset the overview mode image.
376 overview_mode_image_ = gfx::ImageSkia();
381 void WebActivity::CreateOverviewModeImage() {
382 // TODO(skuhne): Create an overview.
385 gfx::ImageSkia WebActivity::GetOverviewModeImage() {
386 return overview_mode_image_;
389 void WebActivity::TitleWasSet(content::NavigationEntry* entry,
391 ActivityManager::Get()->UpdateActivity(this);
394 void WebActivity::DidUpdateFaviconURL(
395 const std::vector<content::FaviconURL>& candidates) {
396 ActivityManager::Get()->UpdateActivity(this);
399 void WebActivity::DidChangeThemeColor(SkColor theme_color) {
400 title_color_ = theme_color;
403 } // namespace athena