blob: 7ec7320d9e6a8b1607f5a0b006c0b1a4f55e6318 [file] [log] [blame]
// Copyright 2021 The Chromium 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 "components/pdf/browser/pdf_navigation_throttle.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/check.h"
#include "base/feature_list.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/pdf/browser/pdf_stream_delegate.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.h"
#include "pdf/pdf_features.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
namespace pdf {
namespace {
// Used to scope the posted navigation task to the lifetime of `web_contents_`.
//
// Could use `WebContents::FromFrameTreeNodeId()` instead, but this doesn't work
// with a `MockNavigationHandle`.
class WebContentsLifetimeHelper
: public content::WebContentsUserData<WebContentsLifetimeHelper> {
public:
base::WeakPtr<WebContentsLifetimeHelper> GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}
void OpenUrl(const content::OpenURLParams& params) {
// `MimeHandlerViewGuest` navigates its embedder for calls to
// `WebContents::OpenURL()`, so use `LoadURLWithParams()` directly instead.
web_contents_->GetController().LoadURLWithParams(
content::NavigationController::LoadURLParams(params));
// Note that we don't need to register the stream's URL loader as a
// subresource, as `MimeHandlerViewGuest::ReadyToCommitNavigation()` will
// handle this as soon as we navigate to a non-`kPdfExtensionId` URL.
}
private:
friend class content::WebContentsUserData<WebContentsLifetimeHelper>;
WEB_CONTENTS_USER_DATA_KEY_DECL();
explicit WebContentsLifetimeHelper(content::WebContents* web_contents)
: web_contents_(web_contents) {}
content::WebContents* web_contents_;
base::WeakPtrFactory<WebContentsLifetimeHelper> weak_factory_{this};
};
WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsLifetimeHelper);
} // namespace
// static
std::unique_ptr<content::NavigationThrottle>
PdfNavigationThrottle::MaybeCreateThrottleFor(
content::NavigationHandle* navigation_handle,
std::unique_ptr<PdfStreamDelegate> stream_delegate) {
if (!base::FeatureList::IsEnabled(chrome_pdf::features::kPdfUnseasoned))
return nullptr;
if (navigation_handle->IsInMainFrame())
return nullptr;
return std::make_unique<PdfNavigationThrottle>(navigation_handle,
std::move(stream_delegate));
}
PdfNavigationThrottle::PdfNavigationThrottle(
content::NavigationHandle* navigation_handle,
std::unique_ptr<PdfStreamDelegate> stream_delegate)
: content::NavigationThrottle(navigation_handle),
stream_delegate_(std::move(stream_delegate)) {
DCHECK(stream_delegate_);
}
PdfNavigationThrottle::~PdfNavigationThrottle() = default;
const char* PdfNavigationThrottle::GetNameForLogging() {
return "PdfNavigationThrottle";
}
content::NavigationThrottle::ThrottleCheckResult
PdfNavigationThrottle::WillStartRequest() {
// Ignore unless navigating to the stream URL.
content::WebContents* contents = navigation_handle()->GetWebContents();
if (!contents)
return PROCEED;
const absl::optional<GURL> original_url = stream_delegate_->MapToOriginalUrl(
contents, navigation_handle()->GetURL());
if (!original_url.has_value())
return PROCEED;
// Uses the same pattern as `PDFIFrameNavigationThrottle` to redirect
// navigation to the original URL. We'll use this to navigate to the correct
// origin, while `PdfURLLoaderRequestInterceptor` will intercept the request
// and replace its content.
content::OpenURLParams params =
content::OpenURLParams::FromNavigationHandle(navigation_handle());
params.url = original_url.value();
params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
params.is_renderer_initiated = false;
params.is_pdf = true;
WebContentsLifetimeHelper::CreateForWebContents(contents);
WebContentsLifetimeHelper* helper =
WebContentsLifetimeHelper::FromWebContents(contents);
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&WebContentsLifetimeHelper::OpenUrl,
helper->GetWeakPtr(), std::move(params)));
return CANCEL_AND_IGNORE;
}
} // namespace pdf