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); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?