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