diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 21:00:10 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 21:00:10 +0000 |
commit | 1a3f5318d4704a0ee7db240a5191a855e671a339 (patch) | |
tree | 25cfa37e1944930ae86305458b895a584300d168 /content | |
parent | 72e457db98bc89dcacbf36b856239bb939de9b3d (diff) | |
download | chromium_src-1a3f5318d4704a0ee7db240a5191a855e671a339.zip chromium_src-1a3f5318d4704a0ee7db240a5191a855e671a339.tar.gz chromium_src-1a3f5318d4704a0ee7db240a5191a855e671a339.tar.bz2 |
navigation-controller: Move all logic related to taking screenshots to ScreenshotManager.
BUG=224852
Review URL: https://codereview.chromium.org/13266003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@196828 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
7 files changed, 392 insertions, 244 deletions
diff --git a/content/browser/web_contents/navigation_controller_impl.cc b/content/browser/web_contents/navigation_controller_impl.cc index dd1d1f6..6985360 100644 --- a/content/browser/web_contents/navigation_controller_impl.cc +++ b/content/browser/web_contents/navigation_controller_impl.cc @@ -5,7 +5,6 @@ #include "content/browser/web_contents/navigation_controller_impl.h" #include "base/bind.h" -#include "base/command_line.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_number_conversions.h" // Temporary @@ -22,6 +21,7 @@ #include "content/browser/web_contents/interstitial_page_impl.h" #include "content/browser/web_contents/navigation_entry_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/browser/web_contents/web_contents_screenshot_manager.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" @@ -36,13 +36,11 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/content_client.h" #include "content/public/common/content_constants.h" -#include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "net/base/escape.h" #include "net/base/mime_util.h" #include "net/base/net_util.h" #include "skia/ext/platform_canvas.h" -#include "ui/gfx/codec/png_codec.h" #include "webkit/glue/glue_serialize.h" namespace content { @@ -50,9 +48,6 @@ namespace { const int kInvalidateAll = 0xFFFFFFFF; -// Minimum delay between taking screenshots. -const int kMinScreenshotIntervalMS = 1000; - // Invoked when entries have been pruned, or removed. For example, if the // current entries are [google, digg, yahoo], with the current entry google, // and the user types in cnet, then digg and yahoo are pruned. @@ -223,8 +218,8 @@ NavigationControllerImpl::NavigationControllerImpl( is_initial_navigation_(true), pending_reload_(NO_RELOAD), get_timestamp_callback_(base::Bind(&base::Time::Now)), - ALLOW_THIS_IN_INITIALIZER_LIST(take_screenshot_factory_(this)), - min_screenshot_interval_ms_(kMinScreenshotIntervalMS) { + ALLOW_THIS_IN_INITIALIZER_LIST( + screenshot_manager_(new WebContentsScreenshotManager(this))) { DCHECK(browser_context_); } @@ -480,160 +475,13 @@ int NavigationControllerImpl::GetIndexForOffset(int offset) const { } void NavigationControllerImpl::TakeScreenshot() { - static bool overscroll_enabled = CommandLine::ForCurrentProcess()-> - GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0"; - if (!overscroll_enabled) - return; - - NavigationEntryImpl* entry = - NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()); - if (!entry) - return; - - RenderViewHost* render_view_host = web_contents_->GetRenderViewHost(); - if (!static_cast<RenderViewHostImpl*> - (render_view_host)->overscroll_controller()) { - return; - } - content::RenderWidgetHostView* view = render_view_host->GetView(); - if (!view) - return; - - // Make sure screenshots aren't taken too frequently. - base::Time now = base::Time::Now(); - if (now - last_screenshot_time_ < - base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) { - return; - } - - last_screenshot_time_ = now; - - if (!take_screenshot_callback_.is_null()) - take_screenshot_callback_.Run(render_view_host); - - render_view_host->CopyFromBackingStore(gfx::Rect(), - view->GetViewBounds().size(), - base::Bind(&NavigationControllerImpl::OnScreenshotTaken, - take_screenshot_factory_.GetWeakPtr(), - entry->GetUniqueID())); -} - -void NavigationControllerImpl::OnScreenshotTaken( - int unique_id, - bool success, - const SkBitmap& bitmap) { - NavigationEntryImpl* entry = NULL; - for (NavigationEntries::iterator i = entries_.begin(); - i != entries_.end(); - ++i) { - if ((*i)->GetUniqueID() == unique_id) { - entry = (*i).get(); - break; - } - } - - if (!entry) { - LOG(ERROR) << "Invalid entry with unique id: " << unique_id; - return; - } - - if (!success || bitmap.empty() || bitmap.isNull()) { - ClearScreenshot(entry); - return; - } - - std::vector<unsigned char> data; - if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data)) { - entry->SetScreenshotPNGData(data); - PurgeScreenshotsIfNecessary(); - } else { - ClearScreenshot(entry); - } -} - -bool NavigationControllerImpl::ClearScreenshot(NavigationEntryImpl* entry) { - if (!entry->screenshot()) - return false; - - entry->SetScreenshotPNGData(std::vector<unsigned char>()); - return true; -} - -void NavigationControllerImpl::PurgeScreenshotsIfNecessary() { - // Allow only a certain number of entries to keep screenshots. - const int kMaxScreenshots = 10; - int screenshot_count = GetScreenshotCount(); - if (screenshot_count < kMaxScreenshots) - return; - - const int current = GetCurrentEntryIndex(); - const int num_entries = GetEntryCount(); - int available_slots = kMaxScreenshots; - if (NavigationEntryImpl::FromNavigationEntry( - GetEntryAtIndex(current))->screenshot()) - --available_slots; - - // Keep screenshots closer to the current navigation entry, and purge the ones - // that are farther away from it. So in each step, look at the entries at - // each offset on both the back and forward history, and start counting them - // to make sure that the correct number of screenshots are kept in memory. - // Note that it is possible for some entries to be missing screenshots (e.g. - // when taking the screenshot failed for some reason). So there may be a state - // where there are a lot of entries in the back history, but none of them has - // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots| - // entries in the forward history list. - int back = current - 1; - int forward = current + 1; - while (available_slots > 0 && (back >= 0 || forward < num_entries)) { - if (back >= 0) { - NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( - GetEntryAtIndex(back)); - if (entry->screenshot()) - --available_slots; - --back; - } - - if (available_slots > 0 && forward < num_entries) { - NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( - GetEntryAtIndex(forward)); - if (entry->screenshot()) - --available_slots; - ++forward; - } - } - - // Purge any screenshot at |back| or lower indices, and |forward| or higher - // indices. - - while (screenshot_count > kMaxScreenshots && back >= 0) { - NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( - GetEntryAtIndex(back)); - if (ClearScreenshot(entry)) - --screenshot_count; - --back; - } - - while (screenshot_count > kMaxScreenshots && forward < num_entries) { - NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( - GetEntryAtIndex(forward)); - if (ClearScreenshot(entry)) - --screenshot_count; - ++forward; - } - CHECK_GE(screenshot_count, 0); - CHECK_LE(screenshot_count, kMaxScreenshots); + screenshot_manager_->TakeScreenshot(); } -int NavigationControllerImpl::GetScreenshotCount() const { - int count = 0; - for (NavigationEntries::const_iterator it = entries_.begin(); - it != entries_.end(); ++it) { - NavigationEntryImpl* entry = - NavigationEntryImpl::FromNavigationEntry(it->get()); - if (entry->screenshot()) - count++; - } - return count; +void NavigationControllerImpl::SetScreenshotManager( + WebContentsScreenshotManager* manager) { + screenshot_manager_.reset(manager ? manager : + new WebContentsScreenshotManager(this)); } bool NavigationControllerImpl::CanGoBack() const { @@ -1471,14 +1319,8 @@ void NavigationControllerImpl::PruneAllButActiveInternal() { } } -// Implemented here and not in NavigationEntry because this controller caches -// the total number of screen shots across all entries. void NavigationControllerImpl::ClearAllScreenshots() { - for (NavigationEntries::iterator it = entries_.begin(); - it != entries_.end(); - ++it) - ClearScreenshot(it->get()); - DCHECK_EQ(GetScreenshotCount(), 0); + screenshot_manager_->ClearAllScreenshots(); } void NavigationControllerImpl::SetSessionStorageNamespace( @@ -1825,9 +1667,4 @@ void NavigationControllerImpl::SetGetTimestampCallbackForTest( get_timestamp_callback_ = get_timestamp_callback; } -void NavigationControllerImpl::SetTakeScreenshotCallbackForTest( - const base::Callback<void(RenderViewHost*)>& take_screenshot_callback) { - take_screenshot_callback_ = take_screenshot_callback; -} - } // namespace content diff --git a/content/browser/web_contents/navigation_controller_impl.h b/content/browser/web_contents/navigation_controller_impl.h index 2c21330..5e50d10 100644 --- a/content/browser/web_contents/navigation_controller_impl.h +++ b/content/browser/web_contents/navigation_controller_impl.h @@ -15,13 +15,13 @@ #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_type.h" -class SkBitmap; struct ViewHostMsg_FrameNavigate_Params; namespace content { class NavigationEntryImpl; class RenderViewHost; class WebContentsImpl; +class WebContentsScreenshotManager; class SiteInstance; struct LoadCommittedDetails; @@ -188,13 +188,12 @@ class CONTENT_EXPORT NavigationControllerImpl // Takes a screenshot of the page at the current state. void TakeScreenshot(); - void SetTakeScreenshotCallbackForTest( - const base::Callback<void(RenderViewHost*)>& take_screenshot_callback); - - void set_min_screenshot_interval(int interval_ms) { - DCHECK_GE(interval_ms, 0); - min_screenshot_interval_ms_ = interval_ms; - } + // Sets the screenshot manager for this NavigationControllerImpl. The + // controller takes ownership of the screenshot manager and destroys it when + // a new screenshot-manager is set, or when the controller is destroyed. + // Setting a NULL manager recreates the default screenshot manager and uses + // that. + void SetScreenshotManager(WebContentsScreenshotManager* manager); private: friend class RestoreHelper; @@ -316,24 +315,6 @@ class CONTENT_EXPORT NavigationControllerImpl // specified |offset|. The index returned is not guaranteed to be valid. int GetIndexForOffset(int offset) const; - // The callback invoked when taking the screenshot of the page is complete. - // This sets the screenshot on the navigation entry. - void OnScreenshotTaken(int unique_id, - bool success, - const SkBitmap& bitmap); - - // Removes the screenshot for the entry, returning true if the entry had a - // screenshot. - bool ClearScreenshot(NavigationEntryImpl* entry); - - // The screenshots in the NavigationEntryImpls can accumulate and consume a - // large amount of memory. This function makes sure that the memory - // consumption is within a certain limit. - void PurgeScreenshotsIfNecessary(); - - // Returns the number of entries with screenshots. - int GetScreenshotCount() const; - // --------------------------------------------------------------------------- // The user browser context associated with this controller. @@ -404,18 +385,6 @@ class CONTENT_EXPORT NavigationControllerImpl // Used to get timestamps for newly-created navigation entries. base::Callback<base::Time()> get_timestamp_callback_; - // A callback that gets called before taking the screenshot of the page. This - // is used only for testing. - base::Callback<void(RenderViewHost*)> take_screenshot_callback_; - - // Taking a screenshot can be async. So use a weakptr for the callback to make - // sure that the screenshot completion callback does not trigger on a - // destroyed NavigationControllerImpl. - base::WeakPtrFactory<NavigationControllerImpl> take_screenshot_factory_; - - base::Time last_screenshot_time_; - int min_screenshot_interval_ms_; - // Used to smooth out timestamps from |get_timestamp_callback_|. // Without this, whenever there is a run of redirects or // code-generated navigations, those navigations may occur within @@ -423,6 +392,8 @@ class CONTENT_EXPORT NavigationControllerImpl // the wrong order in the history view. TimeSmoother time_smoother_; + scoped_ptr<WebContentsScreenshotManager> screenshot_manager_; + DISALLOW_COPY_AND_ASSIGN(NavigationControllerImpl); }; diff --git a/content/browser/web_contents/navigation_controller_impl_unittest.cc b/content/browser/web_contents/navigation_controller_impl_unittest.cc index 0f25e2e..320c8aa 100644 --- a/content/browser/web_contents/navigation_controller_impl_unittest.cc +++ b/content/browser/web_contents/navigation_controller_impl_unittest.cc @@ -24,6 +24,7 @@ #include "content/browser/web_contents/navigation_controller_impl.h" #include "content/browser/web_contents/navigation_entry_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/browser/web_contents/web_contents_screenshot_manager.h" #include "content/common/view_messages.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/notification_registrar.h" @@ -69,6 +70,37 @@ bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) { a_bitmap.getSize()) == 0; } +class MockScreenshotManager : public content::WebContentsScreenshotManager { + public: + explicit MockScreenshotManager(content::NavigationControllerImpl* owner) + : content::WebContentsScreenshotManager(owner) { + } + + virtual ~MockScreenshotManager() { + } + + void TakeScreenshotFor(content::NavigationEntryImpl* entry) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); + bitmap.allocPixels(); + bitmap.eraseRGB(0, 0, 0); + OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); + } + + int GetScreenshotCount() { + return content::WebContentsScreenshotManager::GetScreenshotCount(); + } + + private: + // Overridden from content::WebContentsScreenshotManager: + virtual void TakeScreenshotImpl( + content::RenderViewHost* host, + content::NavigationEntryImpl* entry) OVERRIDE { + } + + DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager); +}; + } // namespace namespace content { @@ -3190,11 +3222,6 @@ TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) { TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { NavigationControllerImpl& controller = controller_impl(); - // Prepare some data to use as screenshot for each navigation. - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); - ASSERT_TRUE(bitmap.allocPixels()); - bitmap.eraseRGB(0, 0, 0); NavigationEntryImpl* entry; // Navigate enough times to make sure that some screenshots are purged. @@ -3204,10 +3231,13 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { EXPECT_EQ(i, controller.GetCurrentEntryIndex()); } + MockScreenshotManager* screenshot_manager = + new MockScreenshotManager(&controller); + controller.SetScreenshotManager(screenshot_manager); for (int i = 0; i < controller.GetEntryCount(); ++i) { entry = NavigationEntryImpl::FromNavigationEntry( controller.GetEntryAtIndex(i)); - controller.OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); + screenshot_manager->TakeScreenshotFor(entry); EXPECT_TRUE(entry->screenshot()); } @@ -3215,7 +3245,7 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { EXPECT_EQ(13, controller.GetEntryCount()); entry = NavigationEntryImpl::FromNavigationEntry( controller.GetEntryAtIndex(11)); - controller.OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); + screenshot_manager->TakeScreenshotFor(entry); for (int i = 0; i < 2; ++i) { entry = NavigationEntryImpl::FromNavigationEntry( @@ -3236,14 +3266,14 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { for (int i = 0; i < controller.GetEntryCount() - 1; ++i) { entry = NavigationEntryImpl::FromNavigationEntry( controller.GetEntryAtIndex(i)); - controller.OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); + screenshot_manager->TakeScreenshotFor(entry); } for (int i = 10; i <= 12; ++i) { entry = NavigationEntryImpl::FromNavigationEntry( controller.GetEntryAtIndex(i)); EXPECT_FALSE(entry->screenshot()) << "Screenshot " << i << " not purged"; - controller.OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); + screenshot_manager->TakeScreenshotFor(entry); } // Navigate to index 7 and assign screenshot to all entries. @@ -3253,7 +3283,7 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { for (int i = 0; i < controller.GetEntryCount() - 1; ++i) { entry = NavigationEntryImpl::FromNavigationEntry( controller.GetEntryAtIndex(i)); - controller.OnScreenshotTaken(entry->GetUniqueID(), true, bitmap); + screenshot_manager->TakeScreenshotFor(entry); } for (int i = 0; i < 2; ++i) { @@ -3264,9 +3294,9 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { // Clear all screenshots. EXPECT_EQ(13, controller.GetEntryCount()); - EXPECT_EQ(10, controller.GetScreenshotCount()); + EXPECT_EQ(10, screenshot_manager->GetScreenshotCount()); controller.ClearAllScreenshots(); - EXPECT_EQ(0, controller.GetScreenshotCount()); + EXPECT_EQ(0, screenshot_manager->GetScreenshotCount()); for (int i = 0; i < controller.GetEntryCount(); ++i) { entry = NavigationEntryImpl::FromNavigationEntry( controller.GetEntryAtIndex(i)); diff --git a/content/browser/web_contents/web_contents_screenshot_manager.cc b/content/browser/web_contents/web_contents_screenshot_manager.cc new file mode 100644 index 0000000..9b8750f --- /dev/null +++ b/content/browser/web_contents/web_contents_screenshot_manager.cc @@ -0,0 +1,214 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/web_contents/web_contents_screenshot_manager.h" + +#include "base/command_line.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/web_contents/navigation_controller_impl.h" +#include "content/browser/web_contents/navigation_entry_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/common/content_switches.h" +#include "ui/gfx/codec/png_codec.h" + +namespace { + +// Minimum delay between taking screenshots. +const int kMinScreenshotIntervalMS = 1000; + +} + +namespace content { + +WebContentsScreenshotManager::WebContentsScreenshotManager( + NavigationControllerImpl* owner) + : owner_(owner), + ALLOW_THIS_IN_INITIALIZER_LIST(take_screenshot_factory_(this)), + min_screenshot_interval_ms_(kMinScreenshotIntervalMS) { +} + +WebContentsScreenshotManager::~WebContentsScreenshotManager() { +} + +void WebContentsScreenshotManager::TakeScreenshot() { + static bool overscroll_enabled = CommandLine::ForCurrentProcess()-> + GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0"; + if (!overscroll_enabled) + return; + + NavigationEntryImpl* entry = + NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry()); + if (!entry) + return; + + RenderViewHost* render_view_host = + owner_->web_contents()->GetRenderViewHost(); + if (!static_cast<RenderViewHostImpl*> + (render_view_host)->overscroll_controller()) { + return; + } + content::RenderWidgetHostView* view = render_view_host->GetView(); + if (!view) + return; + + // Make sure screenshots aren't taken too frequently. + base::Time now = base::Time::Now(); + if (now - last_screenshot_time_ < + base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) { + return; + } + + last_screenshot_time_ = now; + + TakeScreenshotImpl(render_view_host, entry); +} + +// Implemented here and not in NavigationEntry because this manager keeps track +// of the total number of screen shots across all entries. +void WebContentsScreenshotManager::ClearAllScreenshots() { + int count = owner_->GetEntryCount(); + for (int i = 0; i < count; ++i) { + ClearScreenshot(NavigationEntryImpl::FromNavigationEntry( + owner_->GetEntryAtIndex(i))); + } + DCHECK_EQ(GetScreenshotCount(), 0); +} + +void WebContentsScreenshotManager::TakeScreenshotImpl( + RenderViewHost* host, + NavigationEntryImpl* entry) { + DCHECK(host && host->GetView()); + DCHECK(entry); + host->CopyFromBackingStore(gfx::Rect(), + host->GetView()->GetViewBounds().size(), + base::Bind(&WebContentsScreenshotManager::OnScreenshotTaken, + take_screenshot_factory_.GetWeakPtr(), + entry->GetUniqueID())); +} + +void WebContentsScreenshotManager::SetMinScreenshotIntervalMS(int interval_ms) { + DCHECK_GE(interval_ms, 0); + min_screenshot_interval_ms_ = interval_ms; +} + +void WebContentsScreenshotManager::OnScreenshotTaken(int unique_id, + bool success, + const SkBitmap& bitmap) { + NavigationEntryImpl* entry = NULL; + int entry_count = owner_->GetEntryCount(); + for (int i = 0; i < entry_count; ++i) { + NavigationEntry* iter = owner_->GetEntryAtIndex(i); + if (iter->GetUniqueID() == unique_id) { + entry = NavigationEntryImpl::FromNavigationEntry(iter); + break; + } + } + + if (!entry) { + LOG(ERROR) << "Invalid entry with unique id: " << unique_id; + return; + } + + if (!success || bitmap.empty() || bitmap.isNull()) { + ClearScreenshot(entry); + return; + } + + std::vector<unsigned char> data; + if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data)) { + entry->SetScreenshotPNGData(data); + PurgeScreenshotsIfNecessary(); + } else { + ClearScreenshot(entry); + } +} + +int WebContentsScreenshotManager::GetScreenshotCount() const { + int screenshot_count = 0; + int entry_count = owner_->GetEntryCount(); + for (int i = 0; i < entry_count; ++i) { + NavigationEntryImpl* entry = + NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i)); + if (entry->screenshot()) + screenshot_count++; + } + return screenshot_count; +} + +bool WebContentsScreenshotManager::ClearScreenshot(NavigationEntryImpl* entry) { + if (!entry->screenshot()) + return false; + + entry->SetScreenshotPNGData(std::vector<unsigned char>()); + return true; +} + +void WebContentsScreenshotManager::PurgeScreenshotsIfNecessary() { + // Allow only a certain number of entries to keep screenshots. + const int kMaxScreenshots = 10; + int screenshot_count = GetScreenshotCount(); + if (screenshot_count < kMaxScreenshots) + return; + + const int current = owner_->GetCurrentEntryIndex(); + const int num_entries = owner_->GetEntryCount(); + int available_slots = kMaxScreenshots; + if (NavigationEntryImpl::FromNavigationEntry( + owner_->GetEntryAtIndex(current))->screenshot()) { + --available_slots; + } + + // Keep screenshots closer to the current navigation entry, and purge the ones + // that are farther away from it. So in each step, look at the entries at + // each offset on both the back and forward history, and start counting them + // to make sure that the correct number of screenshots are kept in memory. + // Note that it is possible for some entries to be missing screenshots (e.g. + // when taking the screenshot failed for some reason). So there may be a state + // where there are a lot of entries in the back history, but none of them has + // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots| + // entries in the forward history list. + int back = current - 1; + int forward = current + 1; + while (available_slots > 0 && (back >= 0 || forward < num_entries)) { + if (back >= 0) { + NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( + owner_->GetEntryAtIndex(back)); + if (entry->screenshot()) + --available_slots; + --back; + } + + if (available_slots > 0 && forward < num_entries) { + NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( + owner_->GetEntryAtIndex(forward)); + if (entry->screenshot()) + --available_slots; + ++forward; + } + } + + // Purge any screenshot at |back| or lower indices, and |forward| or higher + // indices. + while (screenshot_count > kMaxScreenshots && back >= 0) { + NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( + owner_->GetEntryAtIndex(back)); + if (ClearScreenshot(entry)) + --screenshot_count; + --back; + } + + while (screenshot_count > kMaxScreenshots && forward < num_entries) { + NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( + owner_->GetEntryAtIndex(forward)); + if (ClearScreenshot(entry)) + --screenshot_count; + ++forward; + } + CHECK_GE(screenshot_count, 0); + CHECK_LE(screenshot_count, kMaxScreenshots); +} + +} // namespace content diff --git a/content/browser/web_contents/web_contents_screenshot_manager.h b/content/browser/web_contents/web_contents_screenshot_manager.h new file mode 100644 index 0000000..54e58ed --- /dev/null +++ b/content/browser/web_contents/web_contents_screenshot_manager.h @@ -0,0 +1,79 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_SCREENSHOT_MANAGER_H_ +#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_SCREENSHOT_MANAGER_H_ + +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "base/time.h" +#include "content/common/content_export.h" + +class SkBitmap; + +namespace content { + +class NavigationControllerImpl; +class NavigationEntryImpl; +class RenderViewHost; + +// WebContentsScreenshotManager takes care of taking image-captures for the +// current navigation entry of a NavigationControllerImpl, and managing these +// captured images. These image-captures are used for history navigation using +// overscroll gestures. +class CONTENT_EXPORT WebContentsScreenshotManager { + public: + explicit WebContentsScreenshotManager(NavigationControllerImpl* controller); + virtual ~WebContentsScreenshotManager(); + + // Takes a screenshot of the last-committed entry of the controller. + void TakeScreenshot(); + + // Clears screenshots of all navigation entries. + void ClearAllScreenshots(); + + protected: + virtual void TakeScreenshotImpl(RenderViewHost* host, + NavigationEntryImpl* entry); + + NavigationControllerImpl* owner() { return owner_; } + + void SetMinScreenshotIntervalMS(int interval_ms); + + // The callback invoked when taking the screenshot of the page is complete. + // This sets the screenshot on the navigation entry. + void OnScreenshotTaken(int unique_id, + bool success, + const SkBitmap& bitmap); + + // Returns the number of entries with screenshots. + int GetScreenshotCount() const; + + private: + // Removes the screenshot for the entry, returning true if the entry had a + // screenshot. + bool ClearScreenshot(NavigationEntryImpl* entry); + + // The screenshots in the NavigationEntryImpls can accumulate and consume a + // large amount of memory. This function makes sure that the memory + // consumption is within a certain limit. + void PurgeScreenshotsIfNecessary(); + + // The navigation controller that owns this screenshot-manager. + NavigationControllerImpl* owner_; + + // Taking a screenshot can be async. So use a weakptr for the callback to make + // sure that the screenshot completion callback does not trigger on a + // destroyed WebContentsScreenshotManager. + base::WeakPtrFactory<WebContentsScreenshotManager> take_screenshot_factory_; + + base::Time last_screenshot_time_; + int min_screenshot_interval_ms_; + + DISALLOW_COPY_AND_ASSIGN(WebContentsScreenshotManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_SCREENSHOT_MANAGER_H_ diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc index 89cd720..a18b994 100644 --- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc +++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc @@ -13,6 +13,7 @@ #include "content/browser/web_contents/navigation_controller_impl.h" #include "content/browser/web_contents/navigation_entry_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/browser/web_contents/web_contents_screenshot_manager.h" #include "content/public/browser/web_contents_view.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" @@ -27,24 +28,15 @@ namespace content { -// A dummy callback to reset the screenshot-taker callback. -void DummyCallback(RenderViewHost* host) { -} - // This class keeps track of the RenderViewHost whose screenshot was captured. -class ScreenshotTracker { +class ScreenshotTracker : public WebContentsScreenshotManager { public: explicit ScreenshotTracker(NavigationControllerImpl* controller) - : screenshot_taken_for_(NULL), - controller_(controller) { - controller_->SetTakeScreenshotCallbackForTest( - base::Bind(&ScreenshotTracker::TakeScreenshotCallback, - base::Unretained(this))); + : WebContentsScreenshotManager(controller), + screenshot_taken_for_(NULL) { } virtual ~ScreenshotTracker() { - controller_->SetTakeScreenshotCallbackForTest( - base::Bind(&DummyCallback)); } RenderViewHost* screenshot_taken_for() { return screenshot_taken_for_; } @@ -53,20 +45,28 @@ class ScreenshotTracker { screenshot_taken_for_ = NULL; } + void SetScreenshotInterval(int interval_ms) { + SetMinScreenshotIntervalMS(interval_ms); + } + private: - void TakeScreenshotCallback(RenderViewHost* host) { + // Overridden from WebContentsScreenshotManager: + virtual void TakeScreenshotImpl(RenderViewHost* host, + NavigationEntryImpl* entry) OVERRIDE { screenshot_taken_for_ = host; + WebContentsScreenshotManager::TakeScreenshotImpl(host, entry); } RenderViewHost* screenshot_taken_for_; - NavigationControllerImpl* controller_; DISALLOW_COPY_AND_ASSIGN(ScreenshotTracker); }; class WebContentsViewAuraTest : public ContentBrowserTest { public: - WebContentsViewAuraTest() {} + WebContentsViewAuraTest() + : screenshot_manager_(NULL) { + } // Executes the javascript synchronously and makes sure the returned value is // freed properly. @@ -85,6 +85,13 @@ class WebContentsViewAuraTest : public ContentBrowserTest { aura::Window* content = shell()->web_contents()->GetView()->GetContentNativeView(); content->GetRootWindow()->SetHostSize(gfx::Size(800, 600)); + + WebContentsImpl* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + NavigationControllerImpl* controller = &web_contents->GetController(); + + screenshot_manager_ = new ScreenshotTracker(controller); + controller->SetScreenshotManager(screenshot_manager_); } void TestOverscrollNavigation(bool touch_handler) { @@ -190,7 +197,15 @@ class WebContentsViewAuraTest : public ContentBrowserTest { return index; } + protected: + ScreenshotTracker* screenshot_manager() { return screenshot_manager_; } + void set_min_screenshot_interval(int interval_ms) { + screenshot_manager_->SetScreenshotInterval(interval_ms); + } + private: + ScreenshotTracker* screenshot_manager_; + DISALLOW_COPY_AND_ASSIGN(WebContentsViewAuraTest); }; @@ -299,7 +314,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>( web_contents->GetRenderViewHost()); - web_contents->GetController().set_min_screenshot_interval(0); + set_min_screenshot_interval(0); // Do a few navigations initiated by the page. ExecuteSyncJSFunction(view_host, "navigate_next()"); @@ -392,7 +407,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents()); - web_contents->GetController().set_min_screenshot_interval(0); + set_min_screenshot_interval(0); struct { GURL url; @@ -407,7 +422,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, { GURL(), 0 } }; - ScreenshotTracker tracker(&web_contents->GetController()); + screenshot_manager()->Reset(); for (int i = 0; !navigations[i].url.is_empty(); ++i) { // Navigate via the user initiating a navigation from the UI. NavigationController::LoadURLParams params(navigations[i].url); @@ -419,8 +434,8 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, EXPECT_NE(old_host, web_contents->GetRenderViewHost()) << navigations[i].url.spec(); - EXPECT_EQ(old_host, tracker.screenshot_taken_for()); - tracker.Reset(); + EXPECT_EQ(old_host, screenshot_manager()->screenshot_taken_for()); + screenshot_manager()->Reset(); NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtOffset(-1)); @@ -432,7 +447,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, } // Increase the minimum interval between taking screenshots. - web_contents->GetController().set_min_screenshot_interval(60000); + set_min_screenshot_interval(60000); // Navigate again. This should not take any screenshot because of the // increased screenshot interval. @@ -441,7 +456,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, web_contents->GetController().LoadURLWithParams(params); WaitForLoadStop(web_contents); - EXPECT_EQ(NULL, tracker.screenshot_taken_for()); + EXPECT_EQ(NULL, screenshot_manager()->screenshot_taken_for()); } IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, diff --git a/content/content_browser.gypi b/content/content_browser.gypi index b7817dd..cdd609e 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -968,6 +968,8 @@ 'browser/web_contents/web_contents_drag_win.h', 'browser/web_contents/web_contents_impl.cc', 'browser/web_contents/web_contents_impl.h', + 'browser/web_contents/web_contents_screenshot_manager.cc', + 'browser/web_contents/web_contents_screenshot_manager.h', 'browser/web_contents/web_contents_view_android.cc', 'browser/web_contents/web_contents_view_android.h', 'browser/web_contents/web_contents_view_aura.cc', |