- changed component to Framework
- changed title to NetworkService PDFs not loading with <embed> and <object> tag
Add support for MimeHandlerViewInCrossProcessFrame
There are different html tags to embed PDFs into html pages.
With the latest cefclient.exe application (75.0.13 or 75.0.14) the PDFs are not shown anymore if embedded via <embed> or <object> element.
It works with cefclient.exe --disable-features=NetworkService and it works with version 74.X and lower.
Open https://pdfobject.com/static/ in cefclient.exe to reproduce. Not sure if this is Chromium or CEF related.
Comments (17)
-
-
Approach 1
Chrome uses SubframeNavigationFilteringThrottle, created via this call stack, to handle these tags:
> chrome.dll!subresource_filter::SubframeNavigationFilteringThrottle::SubframeNavigationFilteringThrottle(content::NavigationHandle * handle, subresource_filter::AsyncDocumentSubresourceFilter * parent_frame_filter, subresource_filter::SubframeNavigationFilteringThrottle::Delegate * delegate) Line 33 C++ chrome.dll!std::__1::make_unique<subresource_filter::SubframeNavigationFilteringThrottle,content::NavigationHandle *&,subresource_filter::AsyncDocumentSubresourceFilter *&,subresource_filter::ContentSubresourceFilterThrottleManager *>(content::NavigationHandle * & __args, subresource_filter::AsyncDocumentSubresourceFilter * & __args, subresource_filter::ContentSubresourceFilterThrottleManager * && __args) Line 3131 C++ chrome.dll!subresource_filter::ContentSubresourceFilterThrottleManager::MaybeCreateSubframeNavigationFilteringThrottle(content::NavigationHandle * navigation_handle) Line 292 C++ chrome.dll!subresource_filter::ContentSubresourceFilterThrottleManager::MaybeAppendNavigationThrottles(content::NavigationHandle * navigation_handle, std::__1::vector<std::__1::unique_ptr<content::NavigationThrottle,std::__1::default_delete<content::NavigationThrottle> >,std::__1::allocator<std::__1::unique_ptr<content::NavigationThrottle,std::__1::default_delete<content::NavigationThrottle> > > > * throttles) Line 253 C++ chrome.dll!ChromeSubresourceFilterClient::MaybeAppendNavigationThrottles(content::NavigationHandle * navigation_handle, std::__1::vector<std::__1::unique_ptr<content::NavigationThrottle,std::__1::default_delete<content::NavigationThrottle> >,std::__1::allocator<std::__1::unique_ptr<content::NavigationThrottle,std::__1::default_delete<content::NavigationThrottle> > > > * throttles) Line 85 C++ chrome.dll!ChromeContentBrowserClient::CreateThrottlesForNavigation(content::NavigationHandle * handle) Line 4271 C++
The request for the PDF file then continues asynchronously via this code path:
- SubframeNavigationFilteringThrottle::DeferToCalculateLoadPolicy
- NavigationURLLoaderImpl::NavigationURLLoaderImpl
- NavigationURLLoaderImpl::URLLoaderRequestController::Start
- NavigationURLLoaderImpl::URLLoaderRequestController::MaybeStartLoader
- ThrottlingURLLoader::CreateLoaderAndStart
- ThrottlingURLLoader::OnReceiveResponse
- PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse
Then proceeding with the plugin load as documented in CEF’s extension_system.cc.
Approach 2
Running Chrome with
--disable-features=SubresourceFilter
the SubframeNavigationFilteringThrottle class is not used and the PDF request is initiated via RenderFrameHostImpl::BeginNavigation instead. This call originates from the renderer process with the following call stack:> content.dll!content::RenderFrameImpl::BeginNavigationInternal(std::__1::unique_ptr<blink::WebNavigationInfo,std::__1::default_delete<blink::WebNavigationInfo> > info, bool is_history_navigation_in_new_child_frame) Line 7161 C++ content.dll!content::RenderFrameImpl::BeginNavigation(std::__1::unique_ptr<blink::WebNavigationInfo,std::__1::default_delete<blink::WebNavigationInfo> > info) Line 6525 C++ blink_core.dll!blink::LocalFrameClientImpl::BeginNavigation(const blink::ResourceRequest & request, network::mojom::RequestContextFrameType frame_type, blink::Document * origin_document, blink::DocumentLoader * document_loader, blink::WebNavigationType type, blink::NavigationPolicy policy, bool has_transient_activation, blink::WebFrameLoadType frame_load_type, bool is_client_redirect, blink::WebTriggeringEventInfo triggering_event_info, blink::HTMLFormElement * form, blink::ContentSecurityPolicyDisposition should_check_main_world_content_security_policy, mojo::InterfacePtr<blink::mojom::blink::BlobURLToken> blob_url_token, base::TimeTicks input_start_time, const WTF::String & href_translate, blink::WebContentSecurityPolicyList initiator_csp, mojo::InterfacePtr<blink::mojom::blink::NavigationInitiator> navigation_initiator) Line 614 C++ blink_core.dll!blink::FrameLoader::StartNavigation(const blink::FrameLoadRequest & passed_request, blink::WebFrameLoadType frame_load_type) Line 786 C++ blink_core.dll!blink::HTMLFrameOwnerElement::LoadOrRedirectSubframe(const blink::KURL & url, const WTF::AtomicString & frame_name, bool replace_current_item) Line 495 C++ blink_core.dll!blink::HTMLPlugInElement::RequestObjectInternal(const blink::PluginParameters & plugin_params) Line 208 C++ blink_core.dll!blink::HTMLPlugInElement::RequestObject(const blink::PluginParameters & plugin_params) Line 622 C++ blink_core.dll!blink::HTMLEmbedElement::UpdatePluginInternal() Line 178 C++ blink_core.dll!blink::HTMLPlugInElement::UpdatePlugin() Line 304 C++ blink_core.dll!blink::LocalFrameView::UpdatePlugins() Line 1736 C++ blink_core.dll!blink::LocalFrameView::UpdatePluginsTimerFired(blink::TimerBase *) Line 1750 C++
HTMLPlugInElement::RequestObjectInternal calls GetObjectContentType which returns
kExternalPlugin
in Chrome andkPlugin
in CEF (because PluginData::IsExternalPluginMimeType and PluginInfo::MayUseExternalHandler returns false).So far, the fix is as follows:
- Implement ContentBrowserClient::GetPluginMimeTypesWithExternalHandlers.
- Implement ContentRendererClient::IsPluginHandledExternally
- Run with
--enable-features=MimeHandlerViewInCrossProcessFrame
That causes the PDF extension to load in CEF but the PDF plugin still fails to load. This may be due to a dependency on cross-process iframe support (see issue
#2498).Approach 3
Running Chrome with
--disable-features=SubresourceFilter,MimeHandlerViewInCrossProcessFrame
causes GetObjectContentType to returnkPlugin
like in CEF. However, in Chrome the PDF resource is routed to PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse via ThrottlingURLLoader::OnReceiveResponse while in CEF the PDF resource is routed to ResourceLoader::DidReceiveData and MimeHandlerViewContainerBase::DidReceiveData in the renderer process.Chrome has 4 throttles registered in ThrottlingURLLoader:
- safe_browsing::RendererURLLoaderThrottle
- extensions::MimeHandlerViewContainerBase::PluginResourceThrottle
- GoogleURLLoaderThrottle
- content::MimeSniffingThrottle
CEF only has the MimeSniffingThrottle.
So, the fix is to implement ContentRendererClient::CreateURLLoaderThrottleProvider and return a MimeHandlerViewContainerBase::PluginResourceThrottle.
-
Chrome also has a PDFIFrameNavigationThrottle which shows placeholder HTML if the PDF file fails to load. That might be interesting for CEF as well.
-
- changed status to resolved
Fix PDF load in <embed> and <object> tags (fixes issue
#2727, see issue#2622)→ <<cset 15fc7e58a880>>
-
Fix PDF load in <embed> and <object> tags (fixes issue
#2727, see issue#2622)→ <<cset 2c7943b12149>>
-
Fix PDF load in <embed> and <object> tags (fixes issue
#2727, see issue#2622)→ <<cset 84fed5dc0eb6>>
-
- changed status to open
We can use this issue to track support for MimeHandlerViewInCrossProcessFrame (which is currently disabled).
-
- changed title to Add support for MimeHandlerViewInCrossProcessFrame
- marked as enhancement
-
MimeHandlerViewInCrossProcessFrame has been removed in upstream chromium M80+ https://chromium-review.googlesource.com/c/chromium/src/+/1869121
-
Removal of the flag means that we will now need to support this feature instead of disabling it.
-
The problem with Approach 2 above is that
MimeHandlerViewEmbedder::ReadyToCreateMimeHandlerView
is not being called, which causesMimeHandlerViewEmbedder::RenderFrameCreated
to bail out early instead of creating theMimeHandlerViewGuest
.MimeHandlerViewEmbedder::ReadyToCreateMimeHandlerView
should be called byExtensionsGuestViewMessageFilter::ReadyToCreateMimeHandlerView
, which originates fromMimeHandlerViewContainerManager::IsManagedByContainerManager
in the renderer process. However, that method is skipping theReadyToCreateMimeHandlerView
call in CEF becauseinternal_id_
is empty.The
internal_id_
value should arrive viaMimeHandlerViewContainerManager::SetInternalId
which is called fromMimeHandlerViewEmbedder::ReadyToCommitNavigation
in the browser process. However, in CEF theSetInternalId
call never arrives.Root problem: In chrome the
MimeHandlerViewContainerManager
is created viaChromeRenderFrameObserver::OnAssociatedInterfaceRequestForFrame
beforeSetInternalId
is called whereas whereas in CEF theMimeHandlerViewContainerManager
is created viaChromeExtensionsRendererClient::MaybeCreateMimeHandlerView
afterSetInternalId
is called.IsManagedByContainerManager
is then called later on by the same call stack:> libcef.dll!extensions::MimeHandlerViewContainerManager::IsManagedByContainerManager(const blink::WebElement & plugin_element) Line 322 C++ libcef.dll!extensions::MimeHandlerViewContainerManager::CreateFrameContainer(const blink::WebElement & plugin_element, const GURL & resource_url, const std::__1::basic_string<char,std::__1::char_traits<char>,std::__1::allocator<char>> & mime_type, const content::WebPluginInfo & plugin_info) Line 87 C++ libcef.dll!ChromeExtensionsRendererClient::MaybeCreateMimeHandlerView(const blink::WebElement & plugin_element, const GURL & resource_url, const std::__1::basic_string<char,std::__1::char_traits<char>,std::__1::allocator<char>> & mime_type, const content::WebPluginInfo & plugin_info) Line 281 C++ libcef.dll!CefContentRendererClient::IsPluginHandledExternally(content::RenderFrame * render_frame, const blink::WebElement & plugin_element, const GURL & original_url, const std::__1::basic_string<char,std::__1::char_traits<char>,std::__1::allocator<char>> & mime_type) Line 550 C++ content.dll!content::RenderFrameImpl::IsPluginHandledExternally(const blink::WebElement & plugin_element, const blink::WebURL & url, const blink::WebString & suggested_mime_type) Line 4085 C++ blink_core.dll!blink::LocalFrameClientImpl::IsPluginHandledExternally(blink::HTMLPlugInElement & plugin_element, const blink::KURL & resource_url, const WTF::String & suggesed_mime_type) Line 1192 C++ blink_core.dll!blink::HTMLPlugInElement::RequestObject(const blink::PluginParameters & plugin_params) Line 574 C++ blink_core.dll!blink::HTMLEmbedElement::UpdatePluginInternal() Line 177 C++ blink_core.dll!blink::HTMLPlugInElement::UpdatePlugin() Line 245 C++ blink_core.dll!blink::LocalFrameView::UpdatePlugins() Line 1762 C++ blink_core.dll!blink::LocalFrameView::UpdatePluginsTimerFired(blink::TimerBase *) Line 1776 C++
-
Another problem with debugging this issue. When the debugger is attached to the renderer process on Windows (e.g. with
--wait-for-debugger-children=renderer
) the extensionLoadURL
initiated byMimeHandlerViewGuest::DidAttachToEmbedder
(which callsDispatchBeforeUnload
inNavigatorImpl::Navigate
) will hang. This is because theWebContentsImpl::ShouldIgnoreUnresponsiveRenderer
method returns true andNavigatorImpl::OnBeforeUnloadACK
is never called. Normally, a navigation should result in a call toRenderFrameHostImpl::OnBeforeUnloadACK
. However, for some reason this ACK is never received for the extension load (in CEF or chrome) andRenderFrameHostImpl::BeforeUnloadTimeout
is instead called to trigger a timeout, allowing the load to proceed withExtensionURLLoaderFactory::CreateLoaderAndStart
.
-
The next issue is that we no longer are notified of guest view routes via
CefMimeHandlerViewGuestDelegate::OnGuestAttached
which was previously called beforeCefBrowserInfoManager::OnGetNewBrowserInfo
. This causesCefContentRendererClient::MaybeCreateBrowser
to hang on the syncCefProcessHostMsg_GetNewBrowserInfo
message in the extension renderer process, because information for that browser never becomes available. -
It doesn’t appear that
SubresourceFilter
is required for theMimeHandlerViewInCrossProcessFrame
implementation. SinceSubresourceFilter
has a dependency on the component updater service (viaRegisterSubresourceFilterComponent
) it will bail out early inContentSubresourceFilterThrottleManager::ReadyToCommitNavigation
with resultmojom::ActivationLevel::kDisabled
if the data hasn’t already been downloaded. -
- attached subresource_filter_cef.patch
- attached subresource_filter_chrome.patch
The attached patches to CEF and Chromium add support for
SubresourceFilter
and sufficient component installer support to download the filter data on startup. This is for future reference since the changes were not required to resolve this issue.Not included in this patch is
SubresourceRedirectURLLoaderThrottle
which probably also needs to be added inCefURLLoaderThrottleProviderImpl::CreateThrottles
. -
- changed status to resolved
Add support for MimeHandlerViewInCrossProcessFrame (fixes issue
#2727)The PDF loading documentation in extension_system.cc has be updated to describe the new code paths.
To support delivery of input events to the mime handler renderer process it is now necessary to route events via the correct RWHV interface. For Aura-based platforms (Windows/Linux) this means RWHVAura::OnEvent and for macOS this means RWHVMac::RouteOrProcessEvent. Since Aura uses UI event types these have become the source of truth on Aura-based platforms with conversion to Web event types when needed (primarily for OSR).
This change also adds a timeout for CefProcessHostMsg_GetNewBrowserInfo to avoid a hung renderer process if the guest WebContents route is not registered via CefMimeHandlerViewGuestDelegate::OnGuestDetached as expected prior to CefBrowserInfoManager::OnGetNewBrowserInfo being called. This timeout can be disabled for testing purposes by passing the
--disable-new-browser-info-timeout
command-line flag.The
--disable-features=MimeHandlerViewInCrossProcessFrame
command-line flag can be used for a limited time to restore the previous implementation based on BrowserPlugin. That implementation will be deleted starting with the 3897 branch update.Known issues: - ExecuteJavaScript calls on the frame hosting the PDF extension will not be routed to the mime handler renderer process. - The PDF extension will not load successfully if blocked by ChromePluginPlaceholder and then manually continued via the "Run this plugin" context menu option (see https://crbug.com/533069#c41).
→ <<cset a12c2ab3e131>>
-
cefclient: Windows: Pass correct mouse click count value (see issue
#2727)→ <<cset ed39922f85d7>>
- Log in to comment