Continue OnDownloadUpdated notifications after navigation (issue #1833)

This commit is contained in:
Marshall Greenblatt
2016-02-12 17:31:22 -05:00
parent 8972bbfcb6
commit e601e76445
5 changed files with 270 additions and 50 deletions

View File

@@ -1290,6 +1290,7 @@ void CefBrowserHostImpl::DestroyBrowser() {
if (menu_manager_.get())
menu_manager_->Destroy();
FOR_EACH_OBSERVER(Observer, observers_, OnBrowserDestroyed(this));
platform_delegate_->BrowserDestroyed(this);
while (!queued_messages_.empty()) {
@@ -2496,6 +2497,21 @@ bool CefBrowserHostImpl::Send(IPC::Message* message) {
}
}
void CefBrowserHostImpl::AddObserver(Observer* observer) {
CEF_REQUIRE_UIT();
observers_.AddObserver(observer);
}
void CefBrowserHostImpl::RemoveObserver(Observer* observer) {
CEF_REQUIRE_UIT();
observers_.RemoveObserver(observer);
}
bool CefBrowserHostImpl::HasObserver(Observer* observer) const {
CEF_REQUIRE_UIT();
return observers_.HasObserver(observer);
}
// content::WebContentsObserver::OnMessageReceived() message handlers.
// -----------------------------------------------------------------------------

View File

@@ -24,6 +24,7 @@
#include "libcef/common/response_manager.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
#include "content/public/browser/notification_observer.h"
@@ -71,6 +72,18 @@ class CefBrowserHostImpl : public CefBrowserHost,
virtual void OnResponse(const std::string& response) =0;
};
// Interface to implement for observers that wish to be informed of changes
// to the CefBrowserHostImpl. All methods will be called on the UI thread.
class Observer {
public:
// Called before |browser| is destroyed. Any references to |browser| should
// be cleared when this method is called.
virtual void OnBrowserDestroyed(CefBrowserHostImpl* browser) =0;
protected:
virtual ~Observer() {}
};
~CefBrowserHostImpl() override;
// Create a new CefBrowserHostImpl instance.
@@ -420,6 +433,13 @@ class CefBrowserHostImpl : public CefBrowserHost,
// Override to provide a thread safe implementation.
bool Send(IPC::Message* message) override;
// Manage observer objects. The observer must either outlive this object or
// remove itself before destruction. These methods can only be called on the
// UI thread.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
bool HasObserver(Observer* observer) const;
private:
class DevToolsWebContentsObserver;
@@ -570,6 +590,9 @@ class CefBrowserHostImpl : public CefBrowserHost,
// destroyed.
CefDevToolsFrontend* devtools_frontend_;
// Observers that want to be notified of changes to this object.
base::ObserverList<Observer> observers_;
IMPLEMENT_REFCOUNTING(CefBrowserHostImpl);
DISALLOW_COPY_AND_ASSIGN(CefBrowserHostImpl);
};

View File

@@ -5,9 +5,6 @@
#include "libcef/browser/download_manager_delegate.h"
#include "include/cef_download_handler.h"
#include "libcef/browser/browser_context.h"
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/context.h"
#include "libcef/browser/download_item_impl.h"
#include "libcef/browser/thread_util.h"
@@ -26,18 +23,8 @@ using content::DownloadItem;
using content::DownloadManager;
using content::WebContents;
namespace {
// Helper function to retrieve the CefBrowserHostImpl.
CefRefPtr<CefBrowserHostImpl> GetBrowser(DownloadItem* item) {
content::WebContents* contents = item->GetWebContents();
if (!contents)
return NULL;
return CefBrowserHostImpl::GetBrowserForContents(contents).get();
}
// Helper function to retrieve the CefDownloadHandler.
CefRefPtr<CefDownloadHandler> GetDownloadHandler(
CefRefPtr<CefBrowserHostImpl> browser) {
@@ -274,10 +261,8 @@ CefDownloadManagerDelegate::CefDownloadManagerDelegate(
DownloadManager::DownloadVector items;
manager->GetAllDownloads(&items);
DownloadManager::DownloadVector::const_iterator it = items.begin();
for (; it != items.end(); ++it) {
(*it)->AddObserver(this);
observing_.insert(*it);
}
for (; it != items.end(); ++it)
OnDownloadCreated(manager, *it);
}
CefDownloadManagerDelegate::~CefDownloadManagerDelegate() {
@@ -286,10 +271,8 @@ CefDownloadManagerDelegate::~CefDownloadManagerDelegate() {
manager_->RemoveObserver(this);
}
std::set<DownloadItem*>::const_iterator it = observing_.begin();
for (; it != observing_.end(); ++it)
(*it)->RemoveObserver(this);
observing_.clear();
while (!item_browser_map_.empty())
OnDownloadDestroyed(item_browser_map_.begin()->first);
}
void CefDownloadManagerDelegate::OnDownloadUpdated(
@@ -313,16 +296,52 @@ void CefDownloadManagerDelegate::OnDownloadUpdated(
}
void CefDownloadManagerDelegate::OnDownloadDestroyed(
DownloadItem* download) {
download->RemoveObserver(this);
observing_.erase(download);
DownloadItem* item) {
item->RemoveObserver(this);
CefBrowserHostImpl* browser = nullptr;
ItemBrowserMap::iterator it = item_browser_map_.find(item);
DCHECK(it != item_browser_map_.end());
if (it != item_browser_map_.end()) {
browser = it->second;
item_browser_map_.erase(it);
}
if (browser) {
// Determine if any remaining DownloadItems are associated with the same
// browser. If not, then unregister as an observer.
bool has_remaining = false;
ItemBrowserMap::const_iterator it2 = item_browser_map_.begin();
for (; it2 != item_browser_map_.end(); ++it2) {
if (it2->second == browser) {
has_remaining = true;
break;
}
}
if (!has_remaining)
browser->RemoveObserver(this);
}
}
void CefDownloadManagerDelegate::OnDownloadCreated(
DownloadManager* manager,
DownloadItem* item) {
item->AddObserver(this);
observing_.insert(item);
CefBrowserHostImpl* browser = nullptr;
content::WebContents* contents = item->GetWebContents();
if (contents)
browser = CefBrowserHostImpl::GetBrowserForContents(contents).get();
DCHECK(browser);
item_browser_map_.insert(std::make_pair(item, browser));
// Register as an observer so that we can cancel associated DownloadItems when
// the browser is destroyed.
if (!browser->HasObserver(this))
browser->AddObserver(this);
}
void CefDownloadManagerDelegate::ManagerGoingDown(
@@ -379,3 +398,26 @@ void CefDownloadManagerDelegate::GetNextId(
static uint32 next_id = DownloadItem::kInvalidId + 1;
callback.Run(next_id++);
}
void CefDownloadManagerDelegate::OnBrowserDestroyed(
CefBrowserHostImpl* browser) {
ItemBrowserMap::iterator it = item_browser_map_.begin();
for (; it != item_browser_map_.end(); ++it) {
if (it->second == browser) {
// Don't call back into browsers that have been destroyed. We're not
// canceling the download so it will continue silently until it completes
// or until the associated browser context is destroyed.
it->second = nullptr;
}
}
}
CefBrowserHostImpl* CefDownloadManagerDelegate::GetBrowser(DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end())
return it->second;
// An entry should always exist for a DownloadItem.
NOTREACHED();
return nullptr;
}

View File

@@ -8,6 +8,8 @@
#include <set>
#include "libcef/browser/browser_host_impl.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/download_item.h"
@@ -17,15 +19,16 @@
class CefDownloadManagerDelegate
: public content::DownloadItem::Observer,
public content::DownloadManager::Observer,
public content::DownloadManagerDelegate {
public content::DownloadManagerDelegate,
public CefBrowserHostImpl::Observer {
public:
explicit CefDownloadManagerDelegate(content::DownloadManager* manager);
~CefDownloadManagerDelegate() override;
private:
// DownloadItem::Observer methods.
void OnDownloadUpdated(content::DownloadItem* download) override;
void OnDownloadDestroyed(content::DownloadItem* download) override;
void OnDownloadUpdated(content::DownloadItem* item) override;
void OnDownloadDestroyed(content::DownloadItem* item) override;
// DownloadManager::Observer methods.
void OnDownloadCreated(content::DownloadManager* manager,
@@ -38,9 +41,19 @@ class CefDownloadManagerDelegate
const content::DownloadTargetCallback& callback) override;
void GetNextId(const content::DownloadIdCallback& callback) override;
// CefBrowserHostImpl::Observer methods.
void OnBrowserDestroyed(CefBrowserHostImpl* browser) override;
CefBrowserHostImpl* GetBrowser(content::DownloadItem* item);
content::DownloadManager* manager_;
base::WeakPtrFactory<content::DownloadManager> manager_ptr_factory_;
std::set<content::DownloadItem*> observing_;
// Map of DownloadItem to originating CefBrowserHostImpl. Maintaining this
// map is necessary because DownloadItem::GetWebContents() may return NULL if
// the browser navigates while the download is in progress.
typedef std::map<content::DownloadItem*, CefBrowserHostImpl* > ItemBrowserMap;
ItemBrowserMap item_browser_map_;
DISALLOW_COPY_AND_ASSIGN(CefDownloadManagerDelegate);
};