download功能的下载注册与cdp 监听 Observer模式的很好示范
1,DownloadItem中定义Observer,在BrowserHandler中实现
// One DownloadItem per download. This is the model class that stores all the
// state for a download.
class COMPONENTS_DOWNLOAD_EXPORT DownloadItem : public base::SupportsUserData {
public:
// Callback used with AcquireFileAndDeleteDownload().
using AcquireFileCallback = base::OnceCallback<void(const base::FilePath&)>;
using RenameDownloadCallback = base::OnceCallback<void(DownloadRenameResult)>;
............
// Interface that observers of a particular download must implement in order
// to receive updates to the download's status.
class COMPONENTS_DOWNLOAD_EXPORT Observer : public base::CheckedObserver {
public:
virtual void OnDownloadUpdated(DownloadItem* download) {}
virtual void OnDownloadOpened(DownloadItem* download) {}
virtual void OnDownloadRemoved(DownloadItem* download) {}
// Called when the download is being destroyed. This happens after
// every OnDownloadRemoved() as well as when the DownloadManager is going
// down.
virtual void OnDownloadDestroyed(DownloadItem* download) {}
~Observer() override;
};
.......
什么时候调用 AddObserver(browserHandler), 用如下代码
item->AddObserver(this);
void BrowserHandler::DownloadWillBegin(FrameTreeNode* ftn,
download::DownloadItem* item) {
if (!download_events_enabled_)
return;
const std::u16string likely_filename = net::GetSuggestedFilename(
item->GetURL(), item->GetContentDisposition(), std::string(),
item->GetSuggestedFilename(), item->GetMimeType(), "download");
frontend_->DownloadWillBegin(
ftn->current_frame_host()->devtools_frame_token().ToString(),
item->GetGuid(), item->GetURL().spec(),
base::UTF16ToUTF8(likely_filename));
item->AddObserver(this);//========================
pending_downloads_.insert(item);
}
来自:
void WillBeginDownload(download::DownloadCreateInfo* info,
download::DownloadItem* item) {
if (!item) {
return;
}
auto* rfh = static_cast<RenderFrameHostImpl*>(
RenderFrameHost::FromID(info->render_process_id, info->render_frame_id));
FrameTreeNode* ftn =
rfh ? FrameTreeNode::GloballyFindByID(rfh->GetFrameTreeNodeId())
: nullptr;
if (!ftn) {
return;
}
DispatchToAgents(ftn, &protocol::BrowserHandler::DownloadWillBegin, ftn,
item);
DispatchToAgents(ftn, &protocol::PageHandler::DownloadWillBegin, ftn, item);
for (auto* agent_host : BrowserDevToolsAgentHost::Instances()) {
for (auto* browser_handler :
protocol::BrowserHandler::ForAgentHost(agent_host)) {
browser_handler->DownloadWillBegin(ftn, item);//========================
}
}
}
/////////////////////////////////////////////////////////////////////////////
从上面可以看到,对browserHandler的对象获取是通过 RenderFrameHost::FromID(info->render_process_id, info->render_frame_id));实现的。
2, 在 DownloadItemImpl 中存放着 observers
// The views of this item in the download shelf and download contents.
头文件中放着方法:components/download/public/common/download_item.h;DownloadItemImpl 实现了它们
3,更新下载进度条
void DownloadItemImpl::UpdateObservers() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DVLOG(20) << __func__ << "()";
// Nested updates should not be allowed.
DCHECK(!is_updating_observers_);
is_updating_observers_ = true;
for (auto& observer : observers_)
observer.OnDownloadUpdated(this);
is_updating_observers_ = false;
}
3.1 谁调用这个 UpdateObservers,应该是下载管理器。
初始化时注册进那个实现接口的类 BrowserHandler 。在下载管理器知道进度更新时调用 UpdateObservers。
3.2 谁实现这个observer的 OnDownloadUpdate,应该是CDP的browser_handler .
注意这个browser_handler 是在browser进程的content层实现:src\content\browser\devtools\protocol\browser_handler.h
而非 chrome\browser\devtools\protocol\browser_handler.h chrome浏览器的实现;或者headless浏览器的实现 headless\lib\browser\protocol\browser_handler.h
BrowserHandler 确实实现了我们上面的观察者接口: download::DownloadItem::Observer
class BrowserHandler : public DevToolsDomainHandler,
public Browser::Backend,
public download::DownloadItem::Observer {
.......
// DownloadItem::Observer overrides
void OnDownloadUpdated(download::DownloadItem* item) override;
void OnDownloadDestroyed(download::DownloadItem* item) override;
}
具体实现:
void BrowserHandler::OnDownloadUpdated(download::DownloadItem* item) {
std::string state;
switch (item->GetState()) {
case download::DownloadItem::IN_PROGRESS:
state = Browser::DownloadProgress::StateEnum::InProgress;
break;
case download::DownloadItem::COMPLETE:
state = Browser::DownloadProgress::StateEnum::Completed;
break;
case download::DownloadItem::CANCELLED:
case download::DownloadItem::INTERRUPTED:
state = Browser::DownloadProgress::StateEnum::Canceled;
break;
case download::DownloadItem::MAX_DOWNLOAD_STATE:
NOTREACHED();
}
frontend_->DownloadProgress(item->GetGuid(), item->GetTotalBytes(),
item->GetReceivedBytes(), state);
if (state != Browser::DownloadProgress::StateEnum::InProgress) {
item->RemoveObserver(this);
pending_downloads_.erase(item);
}
}