Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / athena / content / web_activity.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 "athena/content/web_activity.h"
6
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"
19
20 namespace athena {
21 namespace {
22
23 class WebActivityController : public AcceleratorHandler {
24  public:
25   enum Command {
26     CMD_BACK,
27     CMD_FORWARD,
28     CMD_RELOAD,
29     CMD_RELOAD_IGNORE_CACHE,
30   };
31
32   explicit WebActivityController(views::WebView* web_view)
33       : web_view_(web_view), reserved_accelerator_enabled_(true) {}
34   virtual ~WebActivityController() {}
35
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,
42          AF_NONE},
43         {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_REFRESH, ui::EF_NONE, CMD_RELOAD,
44          AF_NONE},
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,
48          AF_NONE},
49         {TRIGGER_ON_PRESS, ui::VKEY_BROWSER_BACK, ui::EF_NONE, CMD_BACK,
50          AF_NONE},
51     };
52     accelerator_manager_->RegisterAccelerators(
53         accelerator_data, arraysize(accelerator_data), this);
54   }
55
56   // Methods that are called before and after key events are consumed by the web
57   // contents.
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);
67
68     if (reserved_accelerator_enabled_ &&
69         accelerator_manager_->IsRegistered(accelerator, AF_RESERVED)) {
70       return web_view_->GetFocusManager()->ProcessAccelerator(accelerator);
71     }
72     *is_keyboard_shortcut =
73         accelerator_manager_->IsRegistered(accelerator, AF_NONE);
74     return false;
75   }
76
77   void HandleKeyboardEvent(content::WebContents* source,
78                            const content::NativeWebKeyboardEvent& event) {
79     unhandled_keyboard_event_handler_.HandleKeyboardEvent(
80         event, web_view_->GetFocusManager());
81   }
82
83  private:
84   // AcceleratorHandler:
85   virtual bool IsCommandEnabled(int command_id) const OVERRIDE {
86     switch (command_id) {
87       case CMD_RELOAD:
88         return true;
89       case CMD_BACK:
90         return web_view_->GetWebContents()->GetController().CanGoBack();
91       case CMD_FORWARD:
92         return web_view_->GetWebContents()->GetController().CanGoForward();
93     }
94     return false;
95   }
96
97   virtual bool OnAcceleratorFired(int command_id,
98                                   const ui::Accelerator& accelerator) OVERRIDE {
99     switch (command_id) {
100       case CMD_RELOAD:
101         web_view_->GetWebContents()->GetController().Reload(false);
102         return true;
103       case CMD_RELOAD_IGNORE_CACHE:
104         web_view_->GetWebContents()->GetController().ReloadIgnoringCache(false);
105         return true;
106       case CMD_BACK:
107         web_view_->GetWebContents()->GetController().GoBack();
108         return true;
109       case CMD_FORWARD:
110         web_view_->GetWebContents()->GetController().GoForward();
111         return true;
112     }
113     return false;
114   }
115
116   views::WebView* web_view_;
117   bool reserved_accelerator_enabled_;
118   scoped_ptr<AcceleratorManager> accelerator_manager_;
119   views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
120
121   DISALLOW_COPY_AND_ASSIGN(WebActivityController);
122 };
123
124 const SkColor kDefaultTitleColor = SkColorSetRGB(0xf2, 0xf2, 0xf2);
125 const SkColor kDefaultUnavailableColor = SkColorSetRGB(0xbb, 0x77, 0x77);
126
127 }  // namespace
128
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 {
132  public:
133   AthenaWebView(content::BrowserContext* context)
134       : views::WebView(context), controller_(new WebActivityController(this)),
135         fullscreen_(false) {
136     SetEmbedFullscreenWidgetMode(true);
137     // TODO(skuhne): Add content observer to detect renderer crash and set
138     // content status to unloaded if that happens.
139   }
140
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)));
146   }
147
148   virtual ~AthenaWebView() {}
149
150   void InstallAccelerators() { controller_->InstallAccelerators(); }
151
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.
166   }
167
168   void ReloadContent() {
169     CHECK(evicted_web_contents_.get());
170     scoped_ptr<content::WebContents> replaced_contents(SwapWebContents(
171         evicted_web_contents_.Pass()));
172   }
173
174   // Check if the content got evicted.
175   const bool IsContentEvicted() { return !!evicted_web_contents_.get(); }
176
177   // content::WebContentsDelegate:
178   virtual content::WebContents* OpenURLFromTab(
179       content::WebContents* source,
180       const content::OpenURLParams& params) OVERRIDE {
181     switch(params.disposition) {
182       case CURRENT_TAB: {
183         DCHECK(source == web_contents());
184         content::NavigationController::LoadURLParams load_url_params(
185             params.url);
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();
197       }
198       case NEW_FOREGROUND_TAB:
199       case NEW_BACKGROUND_TAB:
200       case NEW_POPUP:
201       case NEW_WINDOW: {
202         ActivityManager::Get()->AddActivity(
203             ActivityFactory::Get()->CreateWebActivity(browser_context(),
204                                                       params.url));
205         break;
206       }
207       default:
208         break;
209     }
210     // NULL is returned if the URL wasn't opened immediately.
211     return NULL;
212   }
213
214   virtual void AddNewContents(content::WebContents* source,
215                               content::WebContents* new_contents,
216                               WindowOpenDisposition disposition,
217                               const gfx::Rect& initial_pos,
218                               bool user_gesture,
219                               bool* was_blocked) OVERRIDE {
220     ActivityManager::Get()->AddActivity(
221         new WebActivity(new AthenaWebView(new_contents)));
222   }
223
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);
230   }
231
232   virtual void HandleKeyboardEvent(
233       content::WebContents* source,
234       const content::NativeWebKeyboardEvent& event) OVERRIDE {
235     controller_->HandleKeyboardEvent(source, event);
236   }
237
238   virtual void ToggleFullscreenModeForTab(content::WebContents* web_contents,
239                                           bool enter_fullscreen) OVERRIDE {
240     fullscreen_ = enter_fullscreen;
241     GetWidget()->SetFullscreen(fullscreen_);
242   }
243
244   virtual bool IsFullscreenForTabOrPending(
245       const content::WebContents* web_contents) const OVERRIDE {
246     return fullscreen_;
247   }
248
249  private:
250   scoped_ptr<WebActivityController> controller_;
251
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_;
255
256   // TODO(oshima): Find out if we should support window fullscreen.
257   // It may still useful when a user is in split mode.
258   bool fullscreen_;
259
260   DISALLOW_COPY_AND_ASSIGN(AthenaWebView);
261 };
262
263 WebActivity::WebActivity(content::BrowserContext* browser_context,
264                          const GURL& url)
265     : browser_context_(browser_context),
266       url_(url),
267       web_view_(NULL),
268       title_color_(kDefaultTitleColor),
269       current_state_(ACTIVITY_UNLOADED) {
270 }
271
272 WebActivity::WebActivity(AthenaWebView* web_view)
273     : browser_context_(web_view->browser_context()),
274       url_(web_view->GetWebContents()->GetURL()),
275       web_view_(web_view),
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);
280 }
281
282 WebActivity::~WebActivity() {
283   // It is not required to change the activity state to UNLOADED - unless we
284   // would add state observers.
285 }
286
287 ActivityViewModel* WebActivity::GetActivityViewModel() {
288   return this;
289 }
290
291 void WebActivity::SetCurrentState(Activity::ActivityState state) {
292   switch (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();
301       }
302       Observe(web_view_->GetWebContents());
303       break;
304     case ACTIVITY_BACKGROUND_LOW_PRIORITY:
305       DCHECK(ACTIVITY_VISIBLE == current_state_ ||
306              ACTIVITY_INVISIBLE == current_state_);
307       // TODO(skuhne): Do this.
308       break;
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.
313       break;
314     case ACTIVITY_UNLOADED:
315       DCHECK_NE(ACTIVITY_UNLOADED, current_state_);
316       Observe(NULL);
317       web_view_->EvictContent();
318       break;
319   }
320   // Remember the last requested state.
321   current_state_ = state;
322 }
323
324 Activity::ActivityState WebActivity::GetCurrentState() {
325   if (!web_view_ || web_view_->IsContentEvicted()) {
326     DCHECK_EQ(ACTIVITY_UNLOADED, current_state_);
327     return ACTIVITY_UNLOADED;
328   }
329   // TODO(skuhne): This should be controlled by an observer and should not
330   // reside here.
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).
335
336   return current_state_;
337 }
338
339 bool WebActivity::IsVisible() {
340   return web_view_ && web_view_->IsDrawn();
341 }
342
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;
348 }
349
350 void WebActivity::Init() {
351   DCHECK(web_view_);
352   web_view_->InstallAccelerators();
353 }
354
355 SkColor WebActivity::GetRepresentativeColor() const {
356   // TODO(sad): Compute the color from the favicon.
357   return web_view_ ? title_color_ : kDefaultUnavailableColor;
358 }
359
360 base::string16 WebActivity::GetTitle() const {
361   return web_view_ ? base::UTF8ToUTF16(
362                          web_view_->GetWebContents()->GetVisibleURL().host())
363                    : base::string16();
364 }
365
366 bool WebActivity::UsesFrame() const {
367   return true;
368 }
369
370 views::View* WebActivity::GetContentsView() {
371   if (!web_view_) {
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();
377   }
378   return web_view_;
379 }
380
381 void WebActivity::CreateOverviewModeImage() {
382   // TODO(skuhne): Create an overview.
383 }
384
385 gfx::ImageSkia WebActivity::GetOverviewModeImage() {
386   return overview_mode_image_;
387 }
388
389 void WebActivity::TitleWasSet(content::NavigationEntry* entry,
390                               bool explicit_set) {
391   ActivityManager::Get()->UpdateActivity(this);
392 }
393
394 void WebActivity::DidUpdateFaviconURL(
395     const std::vector<content::FaviconURL>& candidates) {
396   ActivityManager::Get()->UpdateActivity(this);
397 }
398
399 void WebActivity::DidChangeThemeColor(SkColor theme_color) {
400   title_color_ = theme_color;
401 }
402
403 }  // namespace athena