File: pdf_navigation_throttle.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (174 lines) | stat: -rw-r--r-- 7,082 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright 2021 The Chromium Authors
// 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 <optional>
#include <utility>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "components/pdf/browser/pdf_stream_delegate.h"
#include "components/pdf/common/constants.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 "net/http/http_response_headers.h"
#include "pdf/pdf_features.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"

namespace pdf {

content::NavigationThrottle::ThrottleCheckResult
PdfNavigationThrottle::WillProcessResponse() {
  // OOPIF PDF viewer only.
  if (!chrome_pdf::features::IsOopifPdfEnabled()) {
    return PROCEED;
  }

  const net::HttpResponseHeaders* response_headers =
      navigation_handle()->GetResponseHeaders();
  if (!response_headers) {
    return PROCEED;
  }

  std::string mime_type;
  response_headers->GetMimeType(&mime_type);
  if (mime_type != kPDFMimeType) {
    return PROCEED;
  }

  // Fenced frames should not be able to load PDFs.
  bool is_sandboxed_pdf = (navigation_handle()->SandboxFlagsToCommit() &
                           network::mojom::WebSandboxFlags::kPlugins) !=
                          network::mojom::WebSandboxFlags::kNone;
  if (!is_sandboxed_pdf) {
    return PROCEED;
  }

  stream_delegate_->OnPdfEmbedderSandboxed(
      navigation_handle()->GetFrameTreeNodeId());
  return ThrottleCheckResult(CANCEL, net::ERR_BLOCKED_BY_CLIENT);
}

PdfNavigationThrottle::PdfNavigationThrottle(
    content::NavigationThrottleRegistry& registry,
    std::unique_ptr<PdfStreamDelegate> stream_delegate)
    : content::NavigationThrottle(registry),
      stream_delegate_(std::move(stream_delegate)) {
  DCHECK(stream_delegate_);
}

PdfNavigationThrottle::~PdfNavigationThrottle() = default;

const char* PdfNavigationThrottle::GetNameForLogging() {
  return "PdfNavigationThrottle";
}

content::NavigationThrottle::ThrottleCheckResult
PdfNavigationThrottle::WillStartRequest() {
  // Intercepts navigations to a PDF stream URL in a PDF content frame and
  // re-navigates to the original PDF URL.

  // The main frame may contain the PDF extension for non-PdfOopif cases; it
  // should never be navigated away from the extension.
  if (navigation_handle()->IsInMainFrame()) {
    return stream_delegate_->ShouldAllowPdfExtensionFrameNavigation(
               navigation_handle())
               ? PROCEED
               : BLOCK_REQUEST;
  }

  // Skip unless navigating to the stream URL.
  const std::optional<GURL> original_url =
      stream_delegate_->MapToOriginalUrl(*navigation_handle());
  if (!original_url.has_value()) {
    // Block any non-PDF navigations in internal PDF extension and content
    // frames. Allow all other navigations to proceed.
    return stream_delegate_->ShouldAllowPdfFrameNavigation(navigation_handle())
               ? PROCEED
               : BLOCK_REQUEST;
  }

  // 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;

  // The parent frame should always exist after main frame navigations are
  // filtered out at the beginning of this method, and it has the expected
  // embedder URL based on the checks in
  // `PdfStreamDelegate::MapToOriginalUrl()`. For the PDF viewer, the parent
  // frame is the PDF extension frame. For Print Preview, the parent frame is
  // the embedder frame.
  content::RenderFrameHost* embedder_frame =
      navigation_handle()->GetParentFrame();
  CHECK(embedder_frame);

  // Reset the source SiteInstance.  This is a workaround for a lifetime bug:
  // leaving the source SiteInstance in OpenURLParams could inadvertently
  // prolong the SiteInstance's lifetime beyond the lifetime of the
  // BrowserContext it's associated with.  The BrowserContext could get
  // destroyed after the task below is scheduled but before it runs (see
  // https://crbug.com/1382761), and even though the task checks if the frame is
  // null to return early in that case, the task's OpenURLParams would only get
  // destroyed and decrement the source SiteInstance's refcount at the time of
  // that early return, which is already after the BrowserContext is destroyed.
  // This can cause logic in the SiteInstance destructor to trip up if it tries
  // to use the SiteInstance's BrowserContext.
  //
  // The source SiteInstance of this navigation should always be SiteInstance of
  // the parent frame's, which is the embedder frame. Hence, if the navigation
  // task does run and does not get canceled due to the embedder frame becoming
  // null, we can restore the source SiteInstance at that point.
  //
  // TODO(crbug.com/40061670): This should be fixed in a more systematic way.
  DCHECK_EQ(params.source_site_instance, embedder_frame->GetSiteInstance());
  params.source_site_instance.reset();

  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE,
      base::BindOnce(
          [](content::GlobalRenderFrameHostId frame_id,
             const content::OpenURLParams& params) {
            auto* embedder_frame = content::RenderFrameHost::FromID(frame_id);
            if (!embedder_frame) {
              return;
            }

            // Restore the source SiteInstance that was cleared out of the
            // original OpenURLParams.
            content::OpenURLParams new_params = params;
            new_params.source_site_instance = embedder_frame->GetSiteInstance();

            // `MimeHandlerViewGuest` navigates its embedder for calls to
            // `WebContents::OpenURL()`, so use `LoadURLWithParams()` directly
            // instead.
            embedder_frame->GetController().LoadURLWithParams(
                content::NavigationController::LoadURLParams(new_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.
          },
          embedder_frame->GetGlobalId(), std::move(params)));
  return CANCEL_AND_IGNORE;
}

}  // namespace pdf