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