diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc index 4dc383e0f6121..57e24095603bc 100644 --- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc +++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc @@ -183,8 +183,8 @@ void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse( stream_manager->IsPendingNativeFallback(frame_tree_node_id_)) { extension_id = extension_misc::kPdfExtensionId; if (chrome_pdf::features::IsOopifPdfEnabled()) { - cached_body = - stream_manager->TakeCachedFallbackBody(frame_tree_node_id_); + cached_body = stream_manager->TakeCachedFallbackBody( + frame_tree_node_id_, response_url); } } } diff --git a/extensions/browser/mime_handler/mime_handler_stream_manager.cc b/extensions/browser/mime_handler/mime_handler_stream_manager.cc index fab27a2f54650..d2f8ef0bc87d7 100644 --- a/extensions/browser/mime_handler/mime_handler_stream_manager.cc +++ b/extensions/browser/mime_handler/mime_handler_stream_manager.cc @@ -339,7 +339,7 @@ void MimeHandlerStreamManager::AbortAndFallbackToNativeHandler( const size_t decoded_body_size = body.is_valid() ? stream_info->stream()->GetCachedBodySize() : 0u; pending_native_fallback_frames_[embedder_ftn] = - CachedFallbackBody{std::move(body), decoded_body_size}; + CachedFallbackBody{std::move(body), decoded_body_size, original_url}; // Re-navigate just the embedder frame -- not the whole WebContents -- // so iframe-hosted MIME handlers fall back without blowing away the @@ -360,12 +360,21 @@ bool MimeHandlerStreamManager::IsPendingNativeFallback( std::optional MimeHandlerStreamManager::TakeCachedFallbackBody( - content::FrameTreeNodeId frame_tree_node_id) { + content::FrameTreeNodeId frame_tree_node_id, + const GURL& response_url) { auto it = pending_native_fallback_frames_.find(frame_tree_node_id); if (it == pending_native_fallback_frames_.end() || !it->second.pipe.is_valid()) { return std::nullopt; } + // The fallback re-navigation may be redirected. Only replay the + // cached body when the final response URL is the URL it was fetched + // from; otherwise force a network refetch so cross-origin bytes are + // never spliced under another origin's committed URL. + if (!response_url.EqualsIgnoringRef(it->second.original_url)) { + it->second.pipe.reset(); + return std::nullopt; + } return std::move(it->second); } diff --git a/extensions/browser/mime_handler/mime_handler_stream_manager.h b/extensions/browser/mime_handler/mime_handler_stream_manager.h index 11a8d33327efa..487b6227857da 100644 --- a/extensions/browser/mime_handler/mime_handler_stream_manager.h +++ b/extensions/browser/mime_handler/mime_handler_stream_manager.h @@ -200,6 +200,10 @@ class MimeHandlerStreamManager struct CachedFallbackBody { mojo::ScopedDataPipeConsumerHandle pipe; size_t decoded_body_size = 0; + // URL the cached body was actually fetched from. The fallback + // re-navigation may be redirected to a different origin; the cached + // body must only be replayed if the final response URL matches. + GURL original_url; }; // Moves out the cached response body associated with the @@ -211,7 +215,8 @@ class MimeHandlerStreamManager // the body. The mark itself is left in place; clearing happens in // `DidFinishNavigation()` / `FrameDeleted()`. std::optional TakeCachedFallbackBody( - content::FrameTreeNodeId frame_tree_node_id); + content::FrameTreeNodeId frame_tree_node_id, + const GURL& response_url); // Returns whether the handler plugin should handle save events. bool PluginCanSave(const content::RenderFrameHost* embedder_host) const;