diff --git a/cef.gyp b/cef.gyp index 960680768..fd357f592 100644 --- a/cef.gyp +++ b/cef.gyp @@ -791,6 +791,8 @@ 'libcef/browser/browser_context.h', 'libcef/browser/browser_host_impl.cc', 'libcef/browser/browser_host_impl.h', + 'libcef/browser/browser_info.cc', + 'libcef/browser/browser_info.h', 'libcef/browser/browser_main.cc', 'libcef/browser/browser_main.h', 'libcef/browser/browser_message_filter.cc', diff --git a/include/capi/cef_browser_capi.h b/include/capi/cef_browser_capi.h index cbe3a0508..b39449ea0 100644 --- a/include/capi/cef_browser_capi.h +++ b/include/capi/cef_browser_capi.h @@ -109,6 +109,13 @@ typedef struct _cef_browser_t { /// int (CEF_CALLBACK *get_identifier)(struct _cef_browser_t* self); + /// + // Returns true (1) if this object is pointing to the same handle as |that| + // object. + /// + int (CEF_CALLBACK *is_same)(struct _cef_browser_t* self, + struct _cef_browser_t* that); + /// // Returns true (1) if the window is a popup window. /// diff --git a/include/capi/cef_render_process_handler_capi.h b/include/capi/cef_render_process_handler_capi.h index 7daa59baa..63e2a5117 100644 --- a/include/capi/cef_render_process_handler_capi.h +++ b/include/capi/cef_render_process_handler_capi.h @@ -72,7 +72,9 @@ typedef struct _cef_render_process_handler_t { struct _cef_render_process_handler_t* self); /// - // Called after a browser has been created. + // Called after a browser has been created. When browsing cross-origin a new + // browser will be created before the old browser with the same identifier is + // destroyed. /// void (CEF_CALLBACK *on_browser_created)( struct _cef_render_process_handler_t* self, diff --git a/include/cef_browser.h b/include/cef_browser.h index 7a28605d3..d9defce97 100644 --- a/include/cef_browser.h +++ b/include/cef_browser.h @@ -117,6 +117,13 @@ class CefBrowser : public virtual CefBase { /*--cef()--*/ virtual int GetIdentifier() =0; + /// + // Returns true if this object is pointing to the same handle as |that| + // object. + /// + /*--cef()--*/ + virtual bool IsSame(CefRefPtr that) =0; + /// // Returns true if the window is a popup window. /// diff --git a/include/cef_render_process_handler.h b/include/cef_render_process_handler.h index 003b46c3d..b77ab19ca 100644 --- a/include/cef_render_process_handler.h +++ b/include/cef_render_process_handler.h @@ -71,7 +71,9 @@ class CefRenderProcessHandler : public virtual CefBase { virtual void OnWebKitInitialized() {} /// - // Called after a browser has been created. + // Called after a browser has been created. When browsing cross-origin a new + // browser will be created before the old browser with the same identifier is + // destroyed. /// /*--cef()--*/ virtual void OnBrowserCreated(CefRefPtr browser) {} diff --git a/libcef/browser/browser_host_impl.cc b/libcef/browser/browser_host_impl.cc index 72e05e436..52153527f 100644 --- a/libcef/browser/browser_host_impl.cc +++ b/libcef/browser/browser_host_impl.cc @@ -9,6 +9,7 @@ #include #include "libcef/browser/browser_context.h" +#include "libcef/browser/browser_info.h" #include "libcef/browser/chrome_scheme_handler.h" #include "libcef/browser/content_browser_client.h" #include "libcef/browser/context.h" @@ -273,9 +274,11 @@ CefRefPtr CefBrowserHost::CreateBrowserSync( _Context->browser_context()->set_use_osr_next_contents_view( CefBrowserHostImpl::IsWindowRenderingDisabled(windowInfo)); - int browser_id = _Context->GetNextBrowserID(); + scoped_refptr info = + CefContentBrowserClient::Get()->CreateBrowserInfo(); + DCHECK(!info->is_popup()); CefRefPtr browser = - CefBrowserHostImpl::Create(windowInfo, settings, client, NULL, browser_id, + CefBrowserHostImpl::Create(windowInfo, settings, client, NULL, info, NULL); if (!url.empty()) browser->LoadURL(CefFrameHostImpl::kMainFrameId, url); @@ -295,9 +298,13 @@ CefRefPtr CefBrowserHostImpl::Create( const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, - int browser_id, + scoped_refptr browser_info, CefWindowHandle opener) { CEF_REQUIRE_UIT(); + DCHECK(browser_info.get()); + + // If |opener| is non-NULL it must be a popup window. + DCHECK(opener == NULL || browser_info->is_popup()); if (web_contents == NULL) { web_contents = content::WebContents::Create( @@ -309,7 +316,7 @@ CefRefPtr CefBrowserHostImpl::Create( CefRefPtr browser = new CefBrowserHostImpl(window_info, settings, client, web_contents, - browser_id, opener); + browser_info, opener); if (!browser->IsWindowRenderingDisabled() && !browser->PlatformCreateWindow()) { return NULL; @@ -327,8 +334,6 @@ CefRefPtr CefBrowserHostImpl::Create( } #endif // OS_WIN - _Context->AddBrowser(browser); - if (client.get()) { CefRefPtr handler = client->GetLifeSpanHandler(); if (handler.get()) @@ -784,8 +789,13 @@ int CefBrowserHostImpl::GetIdentifier() { return browser_id(); } +bool CefBrowserHostImpl::IsSame(CefRefPtr that) { + CefBrowserHostImpl* impl = static_cast(that.get()); + return (impl == this); +} + bool CefBrowserHostImpl::IsPopup() { - return (opener_ != NULL); + return browser_info_->is_popup(); } bool CefBrowserHostImpl::HasDocument() { @@ -927,8 +937,8 @@ void CefBrowserHostImpl::DestroyBrowser() { request_context_proxy_ = NULL; - // Remove the browser from the list maintained by the context. - _Context->RemoveBrowser(this); + CefContentBrowserClient::Get()->RemoveBrowserInfo(browser_info_); + browser_info_->set_browser(NULL); } gfx::NativeView CefBrowserHostImpl::GetContentView() const { @@ -1189,11 +1199,8 @@ void CefBrowserHostImpl::HandleExternalProtocol(const GURL& url) { } } -bool CefBrowserHostImpl::HasIDMatch(int render_process_id, int render_view_id) { - base::AutoLock lock_scope(state_lock_); - if (render_process_id != render_process_id_) - return false; - return (render_view_id == 0 || render_view_id == render_view_id_); +int CefBrowserHostImpl::browser_id() const { + return browser_info_->browser_id(); } GURL CefBrowserHostImpl::GetLoadingURL() { @@ -1451,19 +1458,25 @@ void CefBrowserHostImpl::WebContentsCreated( const GURL& target_url, content::WebContents* new_contents) { CefWindowHandle opener = NULL; - if (source_contents) + scoped_refptr info; + if (source_contents) { opener = GetBrowserForContents(source_contents)->GetWindowHandle(); - CefContentBrowserClient::NewPopupBrowserInfo info; - CefContentBrowserClient::Get()->GetNewPopupBrowserInfo( - new_contents->GetRenderProcessHost()->GetID(), - new_contents->GetRoutingID(), - &info); - DCHECK_GT(info.browser_id, 0); + // Popup windows may not have info yet. + info = CefContentBrowserClient::Get()->GetOrCreateBrowserInfo( + new_contents->GetRenderProcessHost()->GetID(), + new_contents->GetRoutingID()); + DCHECK(info->is_popup()); + } else { + info = CefContentBrowserClient::Get()->GetBrowserInfo( + new_contents->GetRenderProcessHost()->GetID(), + new_contents->GetRoutingID()); + DCHECK(!info->is_popup()); + } CefRefPtr browser = CefBrowserHostImpl::Create( pending_window_info_, pending_settings_, pending_client_, new_contents, - info.browser_id, opener); + info, opener); pending_client_ = NULL; } @@ -1521,7 +1534,24 @@ void CefBrowserHostImpl::RequestMediaAccessPermission( void CefBrowserHostImpl::RenderViewCreated( content::RenderViewHost* render_view_host) { - SetRenderViewHost(render_view_host); + // When navigating cross-origin the new (pending) RenderViewHost will be + // created before the old (current) RenderViewHost is destroyed. It may be + // necessary in the future to track both current and pending render IDs. + browser_info_->set_render_ids(render_view_host->GetProcess()->GetID(), + render_view_host->GetRoutingID()); + + // Update the DevTools URLs, if any. + CefDevToolsDelegate* devtools_delegate = _Context->devtools_delegate(); + if (devtools_delegate) { + base::AutoLock lock_scope(state_lock_); + devtools_url_http_ = + devtools_delegate->GetDevToolsURL(render_view_host, true); + devtools_url_chrome_ = + devtools_delegate->GetDevToolsURL(render_view_host, false); + } + + registrar_->Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, + content::Source(render_view_host)); } void CefBrowserHostImpl::RenderViewDeleted( @@ -1531,12 +1561,6 @@ void CefBrowserHostImpl::RenderViewDeleted( } void CefBrowserHostImpl::RenderViewReady() { - if (IsPopup()) { - CefContentBrowserClient::Get()->ClearNewPopupBrowserInfo( - web_contents()->GetRenderProcessHost()->GetID(), - web_contents()->GetRoutingID()); - } - // Send the queued messages. queue_messages_ = false; while (!queued_messages_.empty()) { @@ -1756,20 +1780,19 @@ void CefBrowserHostImpl::Observe(int type, // CefBrowserHostImpl private methods. // ----------------------------------------------------------------------------- -CefBrowserHostImpl::CefBrowserHostImpl(const CefWindowInfo& window_info, - const CefBrowserSettings& settings, - CefRefPtr client, - content::WebContents* web_contents, - int browser_id, - CefWindowHandle opener) +CefBrowserHostImpl::CefBrowserHostImpl( + const CefWindowInfo& window_info, + const CefBrowserSettings& settings, + CefRefPtr client, + content::WebContents* web_contents, + scoped_refptr browser_info, + CefWindowHandle opener) : content::WebContentsObserver(web_contents), window_info_(window_info), settings_(settings), client_(client), - browser_id_(browser_id), + browser_info_(browser_info), opener_(opener), - render_process_id_(MSG_ROUTING_NONE), - render_view_id_(MSG_ROUTING_NONE), is_loading_(false), can_go_back_(false), can_go_forward_(false), @@ -1780,6 +1803,9 @@ CefBrowserHostImpl::CefBrowserHostImpl(const CefWindowInfo& window_info, is_in_onsetfocus_(false), focus_on_editable_field_(false), file_chooser_pending_(false) { + DCHECK(!browser_info_->browser().get()); + browser_info_->set_browser(this); + web_contents_.reset(web_contents); web_contents->SetDelegate(this); @@ -1791,31 +1817,6 @@ CefBrowserHostImpl::CefBrowserHostImpl(const CefWindowInfo& window_info, placeholder_frame_ = new CefFrameHostImpl(this, CefFrameHostImpl::kInvalidFrameId, true); - - SetRenderViewHost(web_contents->GetRenderViewHost()); -} - -void CefBrowserHostImpl::SetRenderViewHost(content::RenderViewHost* rvh) { - { - base::AutoLock lock_scope(state_lock_); - - render_view_id_ = rvh->GetRoutingID(); - render_process_id_ = rvh->GetProcess()->GetID(); - - // Update the DevTools URLs, if any. - CefDevToolsDelegate* devtools_delegate = _Context->devtools_delegate(); - if (devtools_delegate) { - devtools_url_http_ = devtools_delegate->GetDevToolsURL(rvh, true); - devtools_url_chrome_ = devtools_delegate->GetDevToolsURL(rvh, false); - } - } - - if (!registrar_->IsRegistered( - this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, - content::Source(rvh))) { - registrar_->Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, - content::Source(rvh)); - } } CefRefPtr CefBrowserHostImpl::GetOrCreateFrame( diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index 60d3defdb..feeb100c7 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -45,6 +45,7 @@ class URLRequest; struct Cef_Request_Params; struct Cef_Response_Params; +class CefBrowserInfo; struct CefNavigateParams; class SiteInstance; @@ -83,7 +84,7 @@ class CefBrowserHostImpl : public CefBrowserHost, const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, - int browser_id, + scoped_refptr browser_info, CefWindowHandle opener); // Returns the browser associated with the specified RenderViewHost. @@ -145,6 +146,7 @@ class CefBrowserHostImpl : public CefBrowserHost, virtual void ReloadIgnoreCache() OVERRIDE; virtual void StopLoad() OVERRIDE; virtual int GetIdentifier() OVERRIDE; + virtual bool IsSame(CefRefPtr that) OVERRIDE; virtual bool IsPopup() OVERRIDE; virtual bool HasDocument() OVERRIDE; virtual CefRefPtr GetMainFrame() OVERRIDE; @@ -211,15 +213,10 @@ class CefBrowserHostImpl : public CefBrowserHost, // Handler for URLs involving external protocols. void HandleExternalProtocol(const GURL& url); - // Returns true if this browser matches the specified ID values. If - // |render_view_id| is 0 any browser with the specified |render_process_id| - // will match. - bool HasIDMatch(int render_process_id, int render_view_id); - // Thread safe accessors. const CefBrowserSettings& settings() const { return settings_; } CefRefPtr client() const { return client_; } - int browser_id() const { return browser_id_; } + int browser_id() const; // Returns the URL that is currently loading (or loaded) in the main frame. GURL GetLoadingURL(); @@ -344,12 +341,9 @@ class CefBrowserHostImpl : public CefBrowserHost, const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, - int browser_id, + scoped_refptr browser_info, CefWindowHandle opener); - // Initialize settings based on the specified RenderViewHost. - void SetRenderViewHost(content::RenderViewHost* rvh); - // Updates and returns an existing frame or creates a new frame. Pass // CefFrameHostImpl::kUnspecifiedFrameId for |parent_frame_id| if unknown. CefRefPtr GetOrCreateFrame(int64 frame_id, @@ -432,15 +426,9 @@ class CefBrowserHostImpl : public CefBrowserHost, CefBrowserSettings settings_; CefRefPtr client_; scoped_ptr web_contents_; - int browser_id_; + scoped_refptr browser_info_; CefWindowHandle opener_; - // Unique ids used for routing communication to/from the renderer. We keep a - // copy of them as member variables so that we can locate matching browsers in - // a thread safe manner. All access must be protected by the state lock. - int render_process_id_; - int render_view_id_; - // Used when creating a new popup window. CefWindowInfo pending_window_info_; CefBrowserSettings pending_settings_; diff --git a/libcef/browser/browser_info.cc b/libcef/browser/browser_info.cc new file mode 100644 index 000000000..458a10264 --- /dev/null +++ b/libcef/browser/browser_info.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2012 The Chromium Embedded Framework 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 "libcef/browser/browser_info.h" +#include "ipc/ipc_message.h" + +CefBrowserInfo::CefBrowserInfo(int browser_id, bool is_popup) + : browser_id_(browser_id), + is_popup_(is_popup), + render_process_id_(MSG_ROUTING_NONE), + render_view_id_(MSG_ROUTING_NONE) { + DCHECK_GT(browser_id, 0); +} + +CefBrowserInfo::~CefBrowserInfo() { +} + +void CefBrowserInfo::set_render_ids( + int render_process_id, int render_view_id) { + base::AutoLock lock_scope(lock_); + render_process_id_ = render_process_id; + render_view_id_ = render_view_id; +} + +bool CefBrowserInfo::is_render_id_match( + int render_process_id, int render_view_id) { + base::AutoLock lock_scope(lock_); + if (render_process_id != render_process_id_) + return false; + return (render_view_id == 0 || render_view_id == render_view_id_); +} + +CefRefPtr CefBrowserInfo::browser() { + base::AutoLock lock_scope(lock_); + return browser_; +} + +void CefBrowserInfo::set_browser(CefRefPtr browser) { + base::AutoLock lock_scope(lock_); + browser_ = browser; +} diff --git a/libcef/browser/browser_info.h b/libcef/browser/browser_info.h new file mode 100644 index 000000000..81f58f5e0 --- /dev/null +++ b/libcef/browser/browser_info.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 The Chromium Embedded Framework 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 CEF_LIBCEF_BROWSER_BROWSER_INFO_H_ +#define CEF_LIBCEF_BROWSER_BROWSER_INFO_H_ +#pragma once + +#include "libcef/browser/browser_host_impl.h" +#include "base/memory/ref_counted.h" + +// CefBrowserInfo is used to associate a browser ID and render view/process +// IDs with a particular CefBrowserHostImpl. Render view/process IDs may change +// during the lifetime of a single CefBrowserHostImpl. +// +// CefBrowserInfo objects are managed by CefContentBrowserClient and should not +// be created directly. +class CefBrowserInfo : public base::RefCountedThreadSafe { + public: + CefBrowserInfo(int browser_id, bool is_popup); + virtual ~CefBrowserInfo(); + + int browser_id() const { return browser_id_; }; + bool is_popup() const { return is_popup_; } + + void set_render_ids(int render_process_id, int render_view_id); + + // Returns true if this browser matches the specified ID values. If + // |render_view_id| is 0 any browser with the specified |render_process_id| + // will match. + bool is_render_id_match(int render_process_id, int render_view_id); + + CefRefPtr browser(); + void set_browser(CefRefPtr browser); + + private: + int browser_id_; + bool is_popup_; + + base::Lock lock_; + + // The below members must be protected by |lock_|. + int render_process_id_; + int render_view_id_; + + // May be NULL if the browser has not yet been created or if the browser has + // been destroyed. + CefRefPtr browser_; + + DISALLOW_COPY_AND_ASSIGN(CefBrowserInfo); +}; + +#endif // CEF_LIBCEF_BROWSER_BROWSER_INFO_H_ diff --git a/libcef/browser/browser_message_filter.cc b/libcef/browser/browser_message_filter.cc index 6e0d671b7..938b469d8 100644 --- a/libcef/browser/browser_message_filter.cc +++ b/libcef/browser/browser_message_filter.cc @@ -6,6 +6,7 @@ #include "libcef/browser/browser_message_filter.h" #include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/browser_info.h" #include "libcef/browser/content_browser_client.h" #include "libcef/browser/context.h" #include "libcef/browser/origin_whitelist_impl.h" @@ -68,18 +69,10 @@ void CefBrowserMessageFilter::OnGetNewRenderThreadInfo( void CefBrowserMessageFilter::OnGetNewBrowserInfo( int routing_id, CefProcessHostMsg_GetNewBrowserInfo_Params* params) { - CefRefPtr browser = - CefBrowserHostImpl::GetBrowserByRoutingID(host_->GetID(), routing_id); - if (browser.get()) { - params->browser_id = browser->GetIdentifier(); - params->is_popup = browser->IsPopup(); - } else { - CefContentBrowserClient::NewPopupBrowserInfo info; - CefContentBrowserClient::Get()->GetNewPopupBrowserInfo(host_->GetID(), - routing_id, - &info); - DCHECK_GT(info.browser_id, 0); - params->browser_id = info.browser_id; - params->is_popup = true; - } + // Popup windows may not have info yet. + scoped_refptr info = + CefContentBrowserClient::Get()->GetOrCreateBrowserInfo(host_->GetID(), + routing_id); + params->browser_id = info->browser_id(); + params->is_popup = info->is_popup(); } diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index dc7d2e3dd..42ef672df 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -7,6 +7,7 @@ #include #include "libcef/browser/browser_context.h" +#include "libcef/browser/browser_info.h" #include "libcef/browser/browser_host_impl.h" #include "libcef/browser/browser_main.h" #include "libcef/browser/browser_message_filter.h" @@ -244,9 +245,9 @@ class CefMediaObserver : public content::MediaObserver { content::MediaRequestState state) OVERRIDE {} }; - CefContentBrowserClient::CefContentBrowserClient() - : browser_main_parts_(NULL) { + : browser_main_parts_(NULL), + next_browser_id_(0) { plugin_service_filter_.reset(new CefPluginServiceFilter); content::PluginServiceImpl::GetInstance()->SetFilter( plugin_service_filter_.get()); @@ -261,36 +262,92 @@ CefContentBrowserClient* CefContentBrowserClient::Get() { content::GetContentClient()->browser()); } -void CefContentBrowserClient::GetNewPopupBrowserInfo( - int render_process_id, int render_view_id, NewPopupBrowserInfo* info) { - base::AutoLock lock_scope(new_popup_browser_lock_); +scoped_refptr CefContentBrowserClient::CreateBrowserInfo() { + base::AutoLock lock_scope(browser_info_lock_); - NewPopupBrowserInfoMap::const_iterator it = - new_popup_browser_info_map_.find( - std::make_pair(render_process_id, render_view_id)); - if (it != new_popup_browser_info_map_.end()) { - *info = it->second; - return; - } - - // Create the info now. - NewPopupBrowserInfo new_info; - new_info.browser_id = _Context->GetNextBrowserID(); - new_popup_browser_info_map_.insert( - std::make_pair( - std::make_pair(render_process_id, render_view_id), new_info)); - *info = new_info; + scoped_refptr browser_info = + new CefBrowserInfo(++next_browser_id_, false); + browser_info_list_.push_back(browser_info); + return browser_info; } -void CefContentBrowserClient::ClearNewPopupBrowserInfo(int render_process_id, - int render_view_id) { - base::AutoLock lock_scope(new_popup_browser_lock_); +scoped_refptr + CefContentBrowserClient::GetOrCreateBrowserInfo(int render_process_id, + int render_view_id) { + base::AutoLock lock_scope(browser_info_lock_); - NewPopupBrowserInfoMap::iterator it = - new_popup_browser_info_map_.find( - std::make_pair(render_process_id, render_view_id)); - if (it != new_popup_browser_info_map_.end()) - new_popup_browser_info_map_.erase(it); + BrowserInfoList::const_iterator it = browser_info_list_.begin(); + for (; it != browser_info_list_.end(); ++it) { + const scoped_refptr& browser_info = *it; + if (browser_info->is_render_id_match(render_process_id, render_view_id)) + return browser_info; + } + + // Must be a popup if it hasn't already been created. + scoped_refptr browser_info = + new CefBrowserInfo(++next_browser_id_, true); + browser_info->set_render_ids(render_process_id, render_view_id); + browser_info_list_.push_back(browser_info); + return browser_info; +} + +void CefContentBrowserClient::RemoveBrowserInfo( + scoped_refptr browser_info) { + base::AutoLock lock_scope(browser_info_lock_); + + BrowserInfoList::iterator it = browser_info_list_.begin(); + for (; it != browser_info_list_.end(); ++it) { + if (*it == browser_info) { + browser_info_list_.erase(it); + return; + } + } + + NOTREACHED(); +} + +void CefContentBrowserClient::DestroyAllBrowsers() { + BrowserInfoList list; + + { + base::AutoLock lock_scope(browser_info_lock_); + list = browser_info_list_; + } + + // Destroy any remaining browser windows. + if (!list.empty()) { + BrowserInfoList::iterator it = list.begin(); + for (; it != list.end(); ++it) { + CefRefPtr browser = (*it)->browser(); + if (browser.get()) + browser->DestroyBrowser(); + } + } + +#ifndef NDEBUG + { + // Verify that all browser windows have been destroyed. + base::AutoLock lock_scope(browser_info_lock_); + DCHECK(browser_info_list_.empty()); + } +#endif +} + +scoped_refptr CefContentBrowserClient::GetBrowserInfo( + int render_process_id, int render_view_id) { + base::AutoLock lock_scope(browser_info_lock_); + + BrowserInfoList::const_iterator it = browser_info_list_.begin(); + for (; it != browser_info_list_.end(); ++it) { + const scoped_refptr& browser_info = *it; + if (browser_info->is_render_id_match(render_process_id, render_view_id)) + return browser_info; + } + + DLOG(WARNING) << "No browser info matching process id " << + render_process_id << " and view id " << render_view_id; + + return scoped_refptr(); } content::BrowserMainParts* CefContentBrowserClient::CreateBrowserMainParts( diff --git a/libcef/browser/content_browser_client.h b/libcef/browser/content_browser_client.h index 263bb1a9a..c37970dcf 100644 --- a/libcef/browser/content_browser_client.h +++ b/libcef/browser/content_browser_client.h @@ -6,16 +6,18 @@ #define CEF_LIBCEF_BROWSER_CONTENT_BROWSER_CLIENT_H_ #pragma once +#include #include #include #include -#include #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "content/public/browser/content_browser_client.h" +class CefBrowserInfo; class CefBrowserMainParts; class CefMediaObserver; class CefResourceDispatcherHostDelegate; @@ -37,21 +39,24 @@ class CefContentBrowserClient : public content::ContentBrowserClient { return browser_main_parts_; } + // Methods for managing CefBrowserInfo life span. Do not add new callers of + // these methods. // During popup window creation there is a race between the call to // CefBrowserMessageFilter::OnGetNewBrowserInfo on the IO thread and the call // to CefBrowserHostImpl::WebContentsCreated on the UI thread. To resolve this - // race we create the info when requested for the first time. - class NewPopupBrowserInfo { - public: - NewPopupBrowserInfo() - : browser_id(0) {} - int browser_id; - }; - void GetNewPopupBrowserInfo(int render_process_id, - int render_view_id, - NewPopupBrowserInfo* info); - void ClearNewPopupBrowserInfo(int render_process_id, - int render_view_id); + // race CefBrowserInfo may be created when requested for the first time and + // before the associated CefBrowserHostImpl is created. + scoped_refptr CreateBrowserInfo(); + scoped_refptr GetOrCreateBrowserInfo(int render_process_id, + int render_view_id); + void RemoveBrowserInfo(scoped_refptr browser_info); + void DestroyAllBrowsers(); + + // Retrieves the CefBrowserInfo matching the specified IDs or an empty + // pointer if no match is found. It is allowed to add new callers of this + // method but consider using CefContext::GetBrowserByRoutingID() instead. + scoped_refptr GetBrowserInfo(int render_process_id, + int render_view_id); virtual content::BrowserMainParts* CreateBrowserMainParts( const content::MainFunctionParams& parameters) OVERRIDE; @@ -86,13 +91,12 @@ class CefContentBrowserClient : public content::ContentBrowserClient { scoped_ptr resource_dispatcher_host_delegate_; - base::Lock new_popup_browser_lock_; + base::Lock browser_info_lock_; - // Map of (render_process_id, render_view_id) to info. Access must be - // protected by |new_popup_browser_lock_|. - typedef std::map, NewPopupBrowserInfo> - NewPopupBrowserInfoMap; - NewPopupBrowserInfoMap new_popup_browser_info_map_; + // Access must be protected by |browser_info_lock_|. + typedef std::list > BrowserInfoList; + BrowserInfoList browser_info_list_; + int next_browser_id_; }; #endif // CEF_LIBCEF_BROWSER_CONTENT_BROWSER_CLIENT_H_ diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index 3f6feefc9..e9c8c0440 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -5,6 +5,7 @@ #include "libcef/browser/context.h" #include "libcef/browser/browser_context.h" #include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/browser_info.h" #include "libcef/browser/browser_main.h" #include "libcef/browser/browser_message_loop.h" #include "libcef/browser/content_browser_client.h" @@ -270,53 +271,20 @@ bool CefContext::OnInitThread() { return (base::PlatformThread::CurrentId() == init_thread_id_); } -int CefContext::GetNextBrowserID() { - return next_browser_id_.GetNext() + 1; -} - -void CefContext::AddBrowser(CefRefPtr browser) { - AutoLock lock_scope(this); - - browserlist_.push_back(browser); -} - -void CefContext::RemoveBrowser(CefRefPtr browser) { - AutoLock lock_scope(this); - - BrowserList::iterator it = browserlist_.begin(); - for (; it != browserlist_.end(); ++it) { - if (it->get() == browser.get()) { - browserlist_.erase(it); - break; - } - } -} - -CefRefPtr CefContext::GetBrowserByID(int id) { - AutoLock lock_scope(this); - - BrowserList::const_iterator it = browserlist_.begin(); - for (; it != browserlist_.end(); ++it) { - if (it->get()->browser_id() == id) - return it->get(); - } - - DLOG(ERROR) << "No browser matching unique id " << id; - return NULL; -} - CefRefPtr CefContext::GetBrowserByRoutingID( int render_process_id, int render_view_id) { - AutoLock lock_scope(this); - - BrowserList::const_iterator it = browserlist_.begin(); - for (; it != browserlist_.end(); ++it) { - if (it->get()->HasIDMatch(render_process_id, render_view_id)) - return it->get(); + scoped_refptr info = + CefContentBrowserClient::Get()->GetBrowserInfo(render_process_id, + render_view_id); + if (info.get()) { + CefRefPtr browser = info->browser(); + if (!browser.get()) { + DLOG(WARNING) << "Found browser id " << info->browser_id() << + " but no browser object matching process id " << + render_process_id << " and view id " << render_view_id; + } + return browser; } - - DLOG(ERROR) << "No browser matching process id " << render_process_id << - " and view id " << render_view_id; return NULL; } @@ -363,22 +331,7 @@ void CefContext::FinishShutdownOnUIThread( base::WaitableEvent* uithread_shutdown_event) { CEF_REQUIRE_UIT(); - BrowserList list; - - { - AutoLock lock_scope(this); - if (!browserlist_.empty()) { - list = browserlist_; - browserlist_.clear(); - } - } - - // Destroy any remaining browser windows. - if (!list.empty()) { - BrowserList::iterator it = list.begin(); - for (; it != list.end(); ++it) - (*it)->DestroyBrowser(); - } + CefContentBrowserClient::Get()->DestroyAllBrowsers(); if (trace_subscriber_.get()) trace_subscriber_.reset(NULL); diff --git a/libcef/browser/context.h b/libcef/browser/context.h index e99dc2460..c40d6398a 100644 --- a/libcef/browser/context.h +++ b/libcef/browser/context.h @@ -13,7 +13,6 @@ #include "include/cef_app.h" #include "include/cef_base.h" -#include "base/atomic_sequence_num.h" #include "base/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" @@ -55,13 +54,8 @@ class CefContext : public CefBase { // Returns true if the context is shutting down. bool shutting_down() { return shutting_down_; } - int GetNextBrowserID(); - void AddBrowser(CefRefPtr browser); - void RemoveBrowser(CefRefPtr browser); - CefRefPtr GetBrowserByID(int id); CefRefPtr GetBrowserByRoutingID(int render_process_id, int render_view_id); - BrowserList* GetBrowserList() { return &browserlist_; } // Retrieve the path at which cache data will be stored on disk. If empty, // cache data will be stored in-memory. @@ -95,12 +89,6 @@ class CefContext : public CefBase { FilePath cache_path_; base::ScopedTempDir cache_temp_dir_; - // Map of browsers that currently exist. - BrowserList browserlist_; - - // Used for assigning unique IDs to browser instances. - base::AtomicSequenceNumber next_browser_id_; - scoped_ptr main_delegate_; scoped_ptr main_runner_; scoped_ptr trace_subscriber_; diff --git a/libcef/renderer/browser_impl.cc b/libcef/renderer/browser_impl.cc index 7b073a60b..0e285e62a 100644 --- a/libcef/renderer/browser_impl.cc +++ b/libcef/renderer/browser_impl.cc @@ -131,13 +131,20 @@ void CefBrowserImpl::StopLoad() { int CefBrowserImpl::GetIdentifier() { CEF_REQUIRE_RT_RETURN(0); - return browser_window_id(); + return browser_id(); +} + +bool CefBrowserImpl::IsSame(CefRefPtr that) { + CEF_REQUIRE_RT_RETURN(false); + + CefBrowserImpl* impl = static_cast(that.get()); + return (impl == this); } bool CefBrowserImpl::IsPopup() { CEF_REQUIRE_RT_RETURN(false); - return is_popup_; + return is_popup(); } bool CefBrowserImpl::HasDocument() { @@ -255,7 +262,7 @@ CefBrowserImpl::CefBrowserImpl(content::RenderView* render_view, int browser_id, bool is_popup) : content::RenderViewObserver(render_view), - browser_window_id_(browser_id), + browser_id_(browser_id), is_popup_(is_popup), last_focused_frame_id_(kInvalidFrameId) { response_manager_.reset(new CefResponseManager); diff --git a/libcef/renderer/browser_impl.h b/libcef/renderer/browser_impl.h index d23f43f9a..485abc16c 100644 --- a/libcef/renderer/browser_impl.h +++ b/libcef/renderer/browser_impl.h @@ -63,6 +63,7 @@ class CefBrowserImpl : public CefBrowser, virtual void ReloadIgnoreCache() OVERRIDE; virtual void StopLoad() OVERRIDE; virtual int GetIdentifier() OVERRIDE; + virtual bool IsSame(CefRefPtr that) OVERRIDE; virtual bool IsPopup() OVERRIDE; virtual bool HasDocument() OVERRIDE; virtual CefRefPtr GetMainFrame() OVERRIDE; @@ -96,7 +97,8 @@ class CefBrowserImpl : public CefBrowser, // Frame objects will be deleted immediately before the frame is closed. void AddFrameObject(int64 frame_id, CefTrackNode* tracked_object); - int browser_window_id() const { return browser_window_id_; } + int browser_id() const { return browser_id_; } + bool is_popup() const { return is_popup_; } content::RenderView* render_view() { return content::RenderViewObserver::render_view(); } @@ -117,8 +119,10 @@ class CefBrowserImpl : public CefBrowser, void OnResponse(const Cef_Response_Params& params); void OnResponseAck(int request_id); - // Id number of browser window which RenderView is attached to. - int browser_window_id_; + // ID of the browser that this RenderView is associated with. During loading + // of cross-origin requests multiple RenderViews may be associated with the + // same browser ID. + int browser_id_; bool is_popup_; // Id of the last frame that had focus. diff --git a/libcef_dll/cpptoc/browser_cpptoc.cc b/libcef_dll/cpptoc/browser_cpptoc.cc index 6d024f278..ba4275a9e 100644 --- a/libcef_dll/cpptoc/browser_cpptoc.cc +++ b/libcef_dll/cpptoc/browser_cpptoc.cc @@ -145,6 +145,26 @@ int CEF_CALLBACK browser_get_identifier(struct _cef_browser_t* self) { return _retval; } +int CEF_CALLBACK browser_is_same(struct _cef_browser_t* self, + struct _cef_browser_t* that) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: that; type: refptr_same + DCHECK(that); + if (!that) + return 0; + + // Execute + bool _retval = CefBrowserCppToC::Get(self)->IsSame( + CefBrowserCppToC::Unwrap(that)); + + // Return type: bool + return _retval; +} + int CEF_CALLBACK browser_is_popup(struct _cef_browser_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -350,6 +370,7 @@ CefBrowserCppToC::CefBrowserCppToC(CefBrowser* cls) struct_.struct_.reload_ignore_cache = browser_reload_ignore_cache; struct_.struct_.stop_load = browser_stop_load; struct_.struct_.get_identifier = browser_get_identifier; + struct_.struct_.is_same = browser_is_same; struct_.struct_.is_popup = browser_is_popup; struct_.struct_.has_document = browser_has_document; struct_.struct_.get_main_frame = browser_get_main_frame; diff --git a/libcef_dll/ctocpp/browser_ctocpp.cc b/libcef_dll/ctocpp/browser_ctocpp.cc index 3cc245e40..46a377343 100644 --- a/libcef_dll/ctocpp/browser_ctocpp.cc +++ b/libcef_dll/ctocpp/browser_ctocpp.cc @@ -134,6 +134,25 @@ int CefBrowserCToCpp::GetIdentifier() { return _retval; } +bool CefBrowserCToCpp::IsSame(CefRefPtr that) { + if (CEF_MEMBER_MISSING(struct_, is_same)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: that; type: refptr_same + DCHECK(that.get()); + if (!that.get()) + return false; + + // Execute + int _retval = struct_->is_same(struct_, + CefBrowserCToCpp::Unwrap(that)); + + // Return type: bool + return _retval?true:false; +} + bool CefBrowserCToCpp::IsPopup() { if (CEF_MEMBER_MISSING(struct_, is_popup)) return false; diff --git a/libcef_dll/ctocpp/browser_ctocpp.h b/libcef_dll/ctocpp/browser_ctocpp.h index 9b0375243..b2f2186e1 100644 --- a/libcef_dll/ctocpp/browser_ctocpp.h +++ b/libcef_dll/ctocpp/browser_ctocpp.h @@ -45,6 +45,7 @@ class CefBrowserCToCpp virtual void ReloadIgnoreCache() OVERRIDE; virtual void StopLoad() OVERRIDE; virtual int GetIdentifier() OVERRIDE; + virtual bool IsSame(CefRefPtr that) OVERRIDE; virtual bool IsPopup() OVERRIDE; virtual bool HasDocument() OVERRIDE; virtual CefRefPtr GetMainFrame() OVERRIDE; diff --git a/tests/unittests/navigation_unittest.cc b/tests/unittests/navigation_unittest.cc index 0cd0fdea2..c7fe5cf83 100644 --- a/tests/unittests/navigation_unittest.cc +++ b/tests/unittests/navigation_unittest.cc @@ -2,6 +2,7 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. +#include #include "include/cef_callback.h" #include "include/cef_scheme.h" #include "tests/cefclient/client_app.h" @@ -76,12 +77,14 @@ class HistoryNavRendererTest : public ClientApp::RenderDelegate { virtual void OnRenderThreadCreated( CefRefPtr app, CefRefPtr extra_info) OVERRIDE { - // Check that the test should be run. - CefRefPtr command_line = - CefCommandLine::GetGlobalCommandLine(); - const std::string& test = command_line->GetSwitchValue("test"); - if (test != kHistoryNavMsg) - return; + if (!g_history_nav_test) { + // Check that the test should be run. + CefRefPtr command_line = + CefCommandLine::GetGlobalCommandLine(); + const std::string& test = command_line->GetSwitchValue("test"); + if (test != kHistoryNavMsg) + return; + } run_test_ = true; } @@ -814,12 +817,14 @@ class OrderNavRendererTest : public ClientApp::RenderDelegate { virtual void OnRenderThreadCreated( CefRefPtr app, CefRefPtr extra_info) OVERRIDE { - // Check that the test should be run. - CefRefPtr command_line = - CefCommandLine::GetGlobalCommandLine(); - const std::string& test = command_line->GetSwitchValue("test"); - if (test != kOrderNavMsg) - return; + if (!g_order_nav_test) { + // Check that the test should be run. + CefRefPtr command_line = + CefCommandLine::GetGlobalCommandLine(); + const std::string& test = command_line->GetSwitchValue("test"); + if (test != kOrderNavMsg) + return; + } run_test_ = true; @@ -1132,11 +1137,315 @@ TEST(NavigationTest, Order) { } +namespace { + +const char kCrossOriginNav1[] = "http://tests-conav1/nav1.html"; +const char kCrossOriginNav2[] = "http://tests-conav2/nav2.html"; +const char kCrossOriginNavMsg[] = "NavigationTest.CrossOriginNav"; + +bool g_cross_origin_nav_test = false; + +// Browser side. +class CrossOriginNavBrowserTest : public ClientApp::BrowserDelegate { + public: + CrossOriginNavBrowserTest() {} + + virtual void OnBeforeChildProcessLaunch( + CefRefPtr app, + CefRefPtr command_line) OVERRIDE { + if (!g_cross_origin_nav_test) + return; + + // Indicate to the render process that the test should be run. + command_line->AppendSwitchWithValue("test", kCrossOriginNavMsg); + } + + protected: + IMPLEMENT_REFCOUNTING(CrossOriginNavBrowserTest); +}; + +// Renderer side. +class CrossOriginNavRendererTest : public ClientApp::RenderDelegate { + public: + CrossOriginNavRendererTest() + : run_test_(false) {} + virtual ~CrossOriginNavRendererTest() { + EXPECT_TRUE(status_list_.empty()); + } + + virtual void OnRenderThreadCreated( + CefRefPtr app, + CefRefPtr extra_info) OVERRIDE { + if (!g_cross_origin_nav_test) { + // Check that the test should be run. + CefRefPtr command_line = + CefCommandLine::GetGlobalCommandLine(); + const std::string& test = command_line->GetSwitchValue("test"); + if (test != kCrossOriginNavMsg) + return; + } + + run_test_ = true; + + EXPECT_FALSE(got_webkit_initialized_); + + got_render_thread_created_.yes(); + } + + virtual void OnWebKitInitialized(CefRefPtr app) OVERRIDE { + if (!run_test_) + return; + + EXPECT_TRUE(got_render_thread_created_); + + got_webkit_initialized_.yes(); + } + + virtual void OnBrowserCreated(CefRefPtr app, + CefRefPtr browser) OVERRIDE { + if (!run_test_) + return; + + EXPECT_TRUE(got_render_thread_created_); + EXPECT_TRUE(got_webkit_initialized_); + + EXPECT_FALSE(GetStatus(browser)); + Status* status = AddStatus(browser); + status->got_browser_created.yes(); + } + + virtual void OnBrowserDestroyed(CefRefPtr app, + CefRefPtr browser) OVERRIDE { + if (!run_test_) + return; + + EXPECT_TRUE(got_render_thread_created_); + EXPECT_TRUE(got_webkit_initialized_); + + Status* status = GetStatus(browser); + EXPECT_TRUE(status); + + EXPECT_TRUE(status->got_browser_created); + EXPECT_TRUE(status->got_before_navigation); + + EXPECT_EQ(status->browser_id, browser->GetIdentifier()); + + EXPECT_TRUE(RemoveStatus(browser)); + } + + virtual bool OnBeforeNavigation(CefRefPtr app, + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + cef_navigation_type_t navigation_type, + bool is_redirect) OVERRIDE { + if (!run_test_) + return false; + + EXPECT_TRUE(got_render_thread_created_); + EXPECT_TRUE(got_webkit_initialized_); + + Status* status = GetStatus(browser); + EXPECT_TRUE(status); + + EXPECT_TRUE(status->got_browser_created); + EXPECT_FALSE(status->got_before_navigation); + + status->got_before_navigation.yes(); + + EXPECT_EQ(status->browser_id, browser->GetIdentifier()); + + SendTestResults(browser); + return false; + } + + protected: + // Send the test results. + void SendTestResults(CefRefPtr browser) { + // Check if the test has failed. + bool result = !TestFailed(); + + // Return the result to the browser process. + CefRefPtr return_msg = + CefProcessMessage::Create(kCrossOriginNavMsg); + CefRefPtr args = return_msg->GetArgumentList(); + EXPECT_TRUE(args.get()); + EXPECT_TRUE(args->SetBool(0, result)); + EXPECT_TRUE(args->SetInt(1, browser->GetIdentifier())); + EXPECT_TRUE(browser->SendProcessMessage(PID_BROWSER, return_msg)); + } + + bool run_test_; + + TrackCallback got_render_thread_created_; + TrackCallback got_webkit_initialized_; + + struct Status { + Status() : browser_id(0) {} + + CefRefPtr browser; + int browser_id; + TrackCallback got_browser_created; + TrackCallback got_before_navigation; + }; + typedef std::list StatusList; + StatusList status_list_; + + Status* GetStatus(CefRefPtr browser) { + StatusList::iterator it = status_list_.begin(); + for (; it != status_list_.end(); ++it) { + Status& status = (*it); + if (status.browser->IsSame(browser)) + return &status; + } + + return NULL; + } + + Status* AddStatus(CefRefPtr browser) { + Status status; + status.browser = browser; + status.browser_id = browser->GetIdentifier(); + EXPECT_GT(status.browser_id, 0); + status_list_.push_back(status); + return &status_list_.back(); + } + + bool RemoveStatus(CefRefPtr browser) { + StatusList::iterator it = status_list_.begin(); + for (; it != status_list_.end(); ++it) { + Status& status = (*it); + if (status.browser->IsSame(browser)) { + status_list_.erase(it); + return true; + } + } + + return false; + } + + IMPLEMENT_REFCOUNTING(CrossOriginNavRendererTest); +}; + +// Browser side. +class CrossOriginNavTestHandler : public TestHandler { + public: + CrossOriginNavTestHandler() + : browser_id_current_(0), + got_message_(false), + got_load_end_(false) {} + + virtual void RunTest() OVERRIDE { + // Add the resources that we will navigate to/from. + AddResource(kCrossOriginNav1, "Nav1", "text/html"); + AddResource(kCrossOriginNav2, "Nav2", "text/html"); + + // Create the browser. + CreateBrowser(kCrossOriginNav1); + } + + void ContinueIfReady(CefRefPtr browser) { + if (!got_message_ || !got_load_end_) + return; + + got_message_ = false; + got_load_end_ = false; + + std::string url = browser->GetMainFrame()->GetURL(); + if (url == kCrossOriginNav1) { + // Load the next url. + browser->GetMainFrame()->LoadURL(kCrossOriginNav2); + } else { + // Done with the test. + DestroyTest(); + } + } + + virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE { + TestHandler::OnAfterCreated(browser); + + EXPECT_EQ(browser_id_current_, 0); + browser_id_current_ = browser->GetIdentifier(); + EXPECT_GT(browser_id_current_, 0); + } + + virtual bool OnBeforeResourceLoad(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) OVERRIDE { + EXPECT_GT(browser_id_current_, 0); + EXPECT_EQ(browser_id_current_, browser->GetIdentifier()); + + return false; + } + + virtual void OnLoadStart(CefRefPtr browser, + CefRefPtr frame) OVERRIDE { + EXPECT_GT(browser_id_current_, 0); + EXPECT_EQ(browser_id_current_, browser->GetIdentifier()); + } + + virtual void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) OVERRIDE { + EXPECT_GT(browser_id_current_, 0); + EXPECT_EQ(browser_id_current_, browser->GetIdentifier()); + + got_load_end_ = true; + ContinueIfReady(browser); + } + + virtual bool OnProcessMessageReceived( + CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) OVERRIDE { + EXPECT_GT(browser_id_current_, 0); + EXPECT_EQ(browser_id_current_, browser->GetIdentifier()); + + const std::string& msg_name = message->GetName(); + if (msg_name == kCrossOriginNavMsg) { + // Test that the renderer side succeeded. + CefRefPtr args = message->GetArgumentList(); + EXPECT_TRUE(args.get()); + EXPECT_TRUE(args->GetBool(0)); + + EXPECT_EQ(browser_id_current_, args->GetInt(1)); + + // Continue with the test. + got_message_ = true; + ContinueIfReady(browser); + + return true; + } + + // Message not handled. + return false; + } + + protected: + int browser_id_current_; + + bool got_message_; + bool got_load_end_; +}; + +} // namespace + +// Verify navigation-related callbacks when browsing cross-origin. +TEST(NavigationTest, CrossOrigin) { + g_cross_origin_nav_test = true; + CefRefPtr handler = + new CrossOriginNavTestHandler(); + handler->ExecuteTest(); + g_cross_origin_nav_test = false; +} + + // Entry point for creating navigation browser test objects. // Called from client_app_delegates.cc. void CreateNavigationBrowserTests(ClientApp::BrowserDelegateSet& delegates) { delegates.insert(new HistoryNavBrowserTest); delegates.insert(new OrderNavBrowserTest); + delegates.insert(new CrossOriginNavBrowserTest); } // Entry point for creating navigation renderer test objects. @@ -1144,4 +1453,5 @@ void CreateNavigationBrowserTests(ClientApp::BrowserDelegateSet& delegates) { void CreateNavigationRendererTests(ClientApp::RenderDelegateSet& delegates) { delegates.insert(new HistoryNavRendererTest); delegates.insert(new OrderNavRendererTest); + delegates.insert(new CrossOriginNavRendererTest); }