File: print_utils.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 (180 lines) | stat: -rw-r--r-- 7,221 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
175
176
177
178
179
180
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/enterprise/data_protection/print_utils.h"

#include <cstring>
#include <optional>
#include <utility>

#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "chrome/browser/profiles/profile.h"
#include "components/device_event_log/device_event_log.h"
#include "components/enterprise/buildflags/buildflags.h"
#include "content/public/browser/web_contents.h"
#include "printing/printing_features.h"

namespace enterprise_data_protection {

namespace {

bool ShouldScan(PrintScanningContext context) {
  switch (context) {
    // For "normal" prints, the scanning can happen immediately after the user
    // clicks "Print" in the print preview dialog as the preview document is
    // representative of what they are printing.
    case PrintScanningContext::kNormalPrintAfterPreview:
      return true;
    case PrintScanningContext::kNormalPrintBeforePrintDocument:
      return false;

    // For "system dialog" prints, the scanning waits until the user picks
    // settings from the system dialog, but starts applying enterprise-logic
    // logic at the `kBeforeSystemDialog` context for that to happen.
    //
    // Scanning also happens right before the document is printed through an
    // existing print job when that is triggered after the print preview
    // dialog.
    case PrintScanningContext::kBeforeSystemDialog:
      return true;
    case PrintScanningContext::kSystemPrintAfterPreview:
      return false;
    case PrintScanningContext::kSystemPrintBeforePrintDocument:
      return true;

#if BUILDFLAG(IS_MAC)
    // For the "Open PDF in Preview" option on Mac, scan right as it happens
    // from the print preview dialog.
    case PrintScanningContext::kOpenPdfInPreview:
      return true;
#endif  // BUILDFLAG(IS_MAC)
  }
}

}  // namespace

void PrintIfAllowedByPolicy(scoped_refptr<base::RefCountedMemory> print_data,
                            content::WebContents* initiator,
                            std::string printer_name,
                            PrintScanningContext context,
                            base::OnceCallback<void(bool)> on_verdict,
                            base::OnceClosure hide_preview) {
  // In some cases like the web contents closing or the render process crashing,
  // tt's possible for `initiator` to be null. In that case, printing can simply
  // be aborted.
  if (!initiator) {
    std::move(on_verdict).Run(/*allowed=*/false);
    return;
  }

  // This needs to be done to avoid having an embedded page compare against
  // policies and report its URL. This is especially important when Chrome's PDF
  // reader is used, as a cryptic extension URL will be sent for
  // scanning/reporting in that case.
  // TODO(b/289243948): Add browser test coverage for web contents received and
  // passed to the delegate.
  content::WebContents* web_contents = initiator->GetOutermostWebContents();

  auto scanning_data = GetPrintAnalysisData(web_contents, context);

  if (!scanning_data) {
    std::move(on_verdict).Run(/*allowed=*/true);
    return;
  }

  // Populate print metadata.
  scanning_data->printer_name = std::move(printer_name);

  // Hide the preview dialog so it doesn't cover the content analysis dialog
  // showing the status of the scanning. Since that scanning dialog only appears
  // when the policy is set to be blocking, don't run `hide_preview` for other
  // policy configurations to avoid races between the dialog being hidden vs
  // destroyed.
  if (scanning_data->settings.block_until_verdict ==
      enterprise_connectors::BlockUntilVerdict::kBlock) {
    std::move(hide_preview).Run();
  }

  PrintIfAllowedByPolicy(print_data, web_contents, std::move(*scanning_data),
                         std::move(on_verdict));
}

void PrintIfAllowedByPolicy(
    scoped_refptr<base::RefCountedMemory> print_data,
    content::WebContents* initiator,
    enterprise_connectors::ContentAnalysisDelegate::Data scanning_data,
    base::OnceCallback<void(bool)> on_verdict) {
  // The preview document bytes are copied so that the content analysis code
  // can arbitrarily use them without having to handle ownership issues with
  // other printing code.
  base::MappedReadOnlyRegion region =
      base::ReadOnlySharedMemoryRegion::Create(print_data->size());
  if (!region.IsValid()) {
    // Allow printing if the scan can't happen due to memory failure.
    PRINTER_LOG(ERROR) << "Printed without analysis due to memory failure";
    std::move(on_verdict).Run(/*allowed=*/true);
    return;
  }
  region.mapping.GetMemoryAsSpan<uint8_t>().copy_prefix_from(*print_data);
  scanning_data.page = std::move(region.region);

  auto on_scan_result = base::BindOnce(
      [](base::OnceCallback<void(bool should_proceed)> callback,
         const enterprise_connectors::ContentAnalysisDelegate::Data& data,
         enterprise_connectors::ContentAnalysisDelegate::Result& result) {
        std::move(callback).Run(result.page_result);
      },
      std::move(on_verdict));

  // This needs to be done to avoid having an embedded page compare against
  // policies and report its URL. This is especially important when Chrome's PDF
  // reader is used, as a cryptic extension URL will be sent for
  // scanning/reporting in that case.
  // TODO(b/289243948): Add browser test coverage for web contents received and
  // passed to the delegate.
  content::WebContents* web_contents = initiator->GetOutermostWebContents();

  enterprise_connectors::ContentAnalysisDelegate::CreateForWebContents(
      web_contents, std::move(scanning_data), std::move(on_scan_result),
      safe_browsing::DeepScanAccessPoint::PRINT);
}

std::optional<enterprise_connectors::ContentAnalysisDelegate::Data>
GetPrintAnalysisData(content::WebContents* web_contents,
                     PrintScanningContext context) {
  enterprise_connectors::ContentAnalysisDelegate::Data scanning_data;

  bool enabled = enterprise_connectors::ContentAnalysisDelegate::IsEnabled(
      Profile::FromBrowserContext(web_contents->GetBrowserContext()),
      web_contents->GetOutermostWebContents()->GetLastCommittedURL(),
      &scanning_data, enterprise_connectors::AnalysisConnector::PRINT);

  if (enabled && ShouldScan(context)) {
    switch (context) {
#if BUILDFLAG(IS_MAC)
      case PrintScanningContext::kOpenPdfInPreview:
#endif  // BUILDFLAG(IS_MAC)
      case PrintScanningContext::kNormalPrintAfterPreview:
      case PrintScanningContext::kNormalPrintBeforePrintDocument:
        scanning_data.reason =
            enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT;
        break;

      case PrintScanningContext::kBeforeSystemDialog:
      case PrintScanningContext::kSystemPrintAfterPreview:
      case PrintScanningContext::kSystemPrintBeforePrintDocument:
        scanning_data.reason =
            enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT;
        break;
    }

    return scanning_data;
  }

  return std::nullopt;
}

}  // namespace enterprise_data_protection