1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/containers/flat_map.h"
13 #include "base/containers/id_map.h"
14 #include "base/feature_list.h"
15 #include "base/lazy_instance.h"
16 #include "base/macros.h"
17 #include "base/memory/ref_counted_memory.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/synchronization/lock.h"
25 #include "base/values.h"
26 #include "build/build_config.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/browser_process_platform_part.h"
29 #include "chrome/browser/printing/background_printing_manager.h"
30 #include "chrome/browser/printing/print_preview_data_service.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/ui/chrome_pages.h"
33 #include "chrome/browser/ui/webui/metrics_handler.h"
34 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
35 #include "chrome/browser/ui/webui/theme_source.h"
36 #include "chrome/browser/ui/webui/webui_util.h"
37 #include "chrome/common/chrome_features.h"
38 #include "chrome/common/pref_names.h"
39 #include "chrome/common/url_constants.h"
40 #include "chrome/grit/browser_resources.h"
41 #include "chrome/grit/chromium_strings.h"
42 #include "chrome/grit/component_extension_resources.h"
43 #include "chrome/grit/generated_resources.h"
44 #include "chrome/grit/print_preview_resources.h"
45 #include "chrome/grit/print_preview_resources_map.h"
46 #include "components/prefs/pref_service.h"
47 #include "components/printing/common/print_messages.h"
48 #include "components/strings/grit/components_strings.h"
49 #include "components/user_manager/user_manager.h"
50 #include "content/public/browser/url_data_source.h"
51 #include "content/public/browser/web_contents.h"
52 #include "content/public/browser/web_ui_data_source.h"
53 #include "extensions/common/constants.h"
54 #include "printing/page_size_margins.h"
55 #include "printing/print_job_constants.h"
56 #include "ui/base/l10n/l10n_util.h"
57 #include "ui/base/ui_base_features.h"
58 #include "ui/base/webui/web_ui_util.h"
59 #include "ui/gfx/geometry/rect.h"
60 #include "ui/web_dialogs/web_dialog_delegate.h"
61 #include "ui/web_dialogs/web_dialog_ui.h"
62
63 #if defined(OS_CHROMEOS)
64 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
65 #elif defined(OS_WIN)
66 #include "base/enterprise_util.h"
67 #endif
68
69 #if !BUILDFLAG(OPTIMIZE_WEBUI)
70 #include "chrome/browser/ui/webui/managed_ui_handler.h"
71 #endif
72
73 using content::WebContents;
74
75 namespace printing {
76
77 namespace {
78
79 #if defined(OS_MACOSX)
80 // U+0028 U+21E7 U+2318 U+0050 U+0029 in UTF8
81 const char kBasicPrintShortcut[] = "\x28\xE2\x8c\xA5\xE2\x8C\x98\x50\x29";
82 #elif !defined(OS_CHROMEOS)
83 const char kBasicPrintShortcut[] = "(Ctrl+Shift+P)";
84 #endif
85
86 #if !BUILDFLAG(OPTIMIZE_WEBUI)
87 constexpr char kGeneratedPath[] =
88 "@out_folder@/gen/chrome/browser/resources/print_preview/";
89 #endif
90
91 PrintPreviewUI::TestDelegate* g_test_delegate = nullptr;
92
93 // Thread-safe wrapper around a base::flat_map to keep track of mappings from
94 // PrintPreviewUI IDs to most recent print preview request IDs.
95 class PrintPreviewRequestIdMapWithLock {
96 public:
PrintPreviewRequestIdMapWithLock()97 PrintPreviewRequestIdMapWithLock() {}
~PrintPreviewRequestIdMapWithLock()98 ~PrintPreviewRequestIdMapWithLock() {}
99
100 // Gets the value for |preview_id|.
101 // Returns true and sets |out_value| on success.
Get(int32_t preview_id,int * out_value)102 bool Get(int32_t preview_id, int* out_value) {
103 base::AutoLock lock(lock_);
104 PrintPreviewRequestIdMap::const_iterator it = map_.find(preview_id);
105 if (it == map_.end())
106 return false;
107 *out_value = it->second;
108 return true;
109 }
110
111 // Sets the |value| for |preview_id|.
Set(int32_t preview_id,int value)112 void Set(int32_t preview_id, int value) {
113 base::AutoLock lock(lock_);
114 map_[preview_id] = value;
115 }
116
117 // Erases the entry for |preview_id|.
Erase(int32_t preview_id)118 void Erase(int32_t preview_id) {
119 base::AutoLock lock(lock_);
120 map_.erase(preview_id);
121 }
122
123 private:
124 // Mapping from PrintPreviewUI ID to print preview request ID.
125 using PrintPreviewRequestIdMap = base::flat_map<int, int>;
126
127 PrintPreviewRequestIdMap map_;
128 base::Lock lock_;
129
130 DISALLOW_COPY_AND_ASSIGN(PrintPreviewRequestIdMapWithLock);
131 };
132
133 // Written to on the UI thread, read from any thread.
134 base::LazyInstance<PrintPreviewRequestIdMapWithLock>::DestructorAtExit
135 g_print_preview_request_id_map = LAZY_INSTANCE_INITIALIZER;
136
137 // PrintPreviewUI IDMap used to avoid exposing raw pointer addresses to WebUI.
138 // Only accessed on the UI thread.
139 base::LazyInstance<base::IDMap<PrintPreviewUI*>>::DestructorAtExit
140 g_print_preview_ui_id_map = LAZY_INSTANCE_INITIALIZER;
141
ShouldHandleRequestCallback(const std::string & path)142 bool ShouldHandleRequestCallback(const std::string& path) {
143 // ChromeWebUIDataSource handles most requests except for the print preview
144 // data.
145 return PrintPreviewUI::ParseDataPath(path, nullptr, nullptr);
146 }
147
148 // Get markup or other resources for the print preview page.
HandleRequestCallback(const std::string & path,content::WebUIDataSource::GotDataCallback callback)149 void HandleRequestCallback(const std::string& path,
150 content::WebUIDataSource::GotDataCallback callback) {
151 // ChromeWebUIDataSource handles most requests except for the print preview
152 // data.
153 int preview_ui_id;
154 int page_index;
155 CHECK(PrintPreviewUI::ParseDataPath(path, &preview_ui_id, &page_index));
156
157 scoped_refptr<base::RefCountedMemory> data;
158 PrintPreviewDataService::GetInstance()->GetDataEntry(preview_ui_id,
159 page_index, &data);
160 if (data.get()) {
161 std::move(callback).Run(data.get());
162 return;
163 }
164 // Invalid request.
165 auto empty_bytes = base::MakeRefCounted<base::RefCountedBytes>();
166 std::move(callback).Run(empty_bytes.get());
167 }
168
AddPrintPreviewStrings(content::WebUIDataSource * source)169 void AddPrintPreviewStrings(content::WebUIDataSource* source) {
170 static constexpr webui::LocalizedString kLocalizedStrings[] = {
171 {"accept", IDS_PRINT_PREVIEW_ACCEPT_INVITE},
172 {"acceptForGroup", IDS_PRINT_PREVIEW_ACCEPT_GROUP_INVITE},
173 {"accountSelectTitle", IDS_PRINT_PREVIEW_ACCOUNT_SELECT_TITLE},
174 {"addAccountTitle", IDS_PRINT_PREVIEW_ADD_ACCOUNT_TITLE},
175 {"advancedSettingsDialogConfirm",
176 IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_DIALOG_CONFIRM},
177 {"advancedSettingsDialogTitle",
178 IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_DIALOG_TITLE},
179 {"advancedSettingsSearchBoxPlaceholder",
180 IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_SEARCH_BOX_PLACEHOLDER},
181 {"bottom", IDS_PRINT_PREVIEW_BOTTOM_MARGIN_LABEL},
182 {"cancel", IDS_CANCEL},
183 {"cloudPrintPromotion", IDS_PRINT_PREVIEW_CLOUD_PRINT_PROMOTION},
184 {"copiesInstruction", IDS_PRINT_PREVIEW_COPIES_INSTRUCTION},
185 {"copiesLabel", IDS_PRINT_PREVIEW_COPIES_LABEL},
186 {"couldNotPrint", IDS_PRINT_PREVIEW_COULD_NOT_PRINT},
187 {"customMargins", IDS_PRINT_PREVIEW_CUSTOM_MARGINS},
188 {"defaultMargins", IDS_PRINT_PREVIEW_DEFAULT_MARGINS},
189 {"destinationLabel", IDS_PRINT_PREVIEW_DESTINATION_LABEL},
190 {"destinationSearchTitle", IDS_PRINT_PREVIEW_DESTINATION_SEARCH_TITLE},
191 {"dpiItemLabel", IDS_PRINT_PREVIEW_DPI_ITEM_LABEL},
192 {"dpiLabel", IDS_PRINT_PREVIEW_DPI_LABEL},
193 {"examplePageRangeText", IDS_PRINT_PREVIEW_EXAMPLE_PAGE_RANGE_TEXT},
194 {"extensionDestinationIconTooltip",
195 IDS_PRINT_PREVIEW_EXTENSION_DESTINATION_ICON_TOOLTIP},
196 {"goBackButton", IDS_PRINT_PREVIEW_BUTTON_GO_BACK},
197 {"groupPrinterSharingInviteText", IDS_PRINT_PREVIEW_GROUP_INVITE_TEXT},
198 {"invalidPrinterSettings", IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS},
199 {"layoutLabel", IDS_PRINT_PREVIEW_LAYOUT_LABEL},
200 {"learnMore", IDS_LEARN_MORE},
201 {"left", IDS_PRINT_PREVIEW_LEFT_MARGIN_LABEL},
202 {"loading", IDS_PRINT_PREVIEW_LOADING},
203 {"manage", IDS_PRINT_PREVIEW_MANAGE},
204 {"managedSettings", IDS_PRINT_PREVIEW_MANAGED_SETTINGS_TEXT},
205 {"marginsLabel", IDS_PRINT_PREVIEW_MARGINS_LABEL},
206 {"mediaSizeLabel", IDS_PRINT_PREVIEW_MEDIA_SIZE_LABEL},
207 {"minimumMargins", IDS_PRINT_PREVIEW_MINIMUM_MARGINS},
208 {"moreOptionsLabel", IDS_MORE_OPTIONS_LABEL},
209 {"newShowAdvancedOptions", IDS_PRINT_PREVIEW_NEW_SHOW_ADVANCED_OPTIONS},
210 {"noAdvancedSettingsMatchSearchHint",
211 IDS_PRINT_PREVIEW_NO_ADVANCED_SETTINGS_MATCH_SEARCH_HINT},
212 {"noDestinationsMessage", IDS_PRINT_PREVIEW_NO_DESTINATIONS_MESSAGE},
213 {"noLongerSupported", IDS_PRINT_PREVIEW_NO_LONGER_SUPPORTED},
214 {"noLongerSupportedFragment",
215 IDS_PRINT_PREVIEW_NO_LONGER_SUPPORTED_FRAGMENT},
216 {"noMargins", IDS_PRINT_PREVIEW_NO_MARGINS},
217 {"noPlugin", IDS_PRINT_PREVIEW_NO_PLUGIN},
218 {"nonIsotropicDpiItemLabel",
219 IDS_PRINT_PREVIEW_NON_ISOTROPIC_DPI_ITEM_LABEL},
220 {"offline", IDS_PRINT_PREVIEW_OFFLINE},
221 {"offlineForMonth", IDS_PRINT_PREVIEW_OFFLINE_FOR_MONTH},
222 {"offlineForWeek", IDS_PRINT_PREVIEW_OFFLINE_FOR_WEEK},
223 {"offlineForYear", IDS_PRINT_PREVIEW_OFFLINE_FOR_YEAR},
224 {"optionAllPages", IDS_PRINT_PREVIEW_OPTION_ALL_PAGES},
225 {"optionBackgroundColorsAndImages",
226 IDS_PRINT_PREVIEW_OPTION_BACKGROUND_COLORS_AND_IMAGES},
227 {"optionBw", IDS_PRINT_PREVIEW_OPTION_BW},
228 {"optionCollate", IDS_PRINT_PREVIEW_OPTION_COLLATE},
229 {"optionColor", IDS_PRINT_PREVIEW_OPTION_COLOR},
230 {"optionCustomPages", IDS_PRINT_PREVIEW_OPTION_CUSTOM_PAGES},
231 {"optionCustomScaling", IDS_PRINT_PREVIEW_OPTION_CUSTOM_SCALING},
232 {"optionDefaultScaling", IDS_PRINT_PREVIEW_OPTION_DEFAULT_SCALING},
233 {"optionFitToPage", IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAGE},
234 {"optionFitToPaper", IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAPER},
235 {"optionHeaderFooter", IDS_PRINT_PREVIEW_OPTION_HEADER_FOOTER},
236 {"optionLandscape", IDS_PRINT_PREVIEW_OPTION_LANDSCAPE},
237 {"optionLongEdge", IDS_PRINT_PREVIEW_OPTION_LONG_EDGE},
238 {"optionPortrait", IDS_PRINT_PREVIEW_OPTION_PORTRAIT},
239 {"optionRasterize", IDS_PRINT_PREVIEW_OPTION_RASTERIZE},
240 {"optionSelectionOnly", IDS_PRINT_PREVIEW_OPTION_SELECTION_ONLY},
241 {"optionShortEdge", IDS_PRINT_PREVIEW_OPTION_SHORT_EDGE},
242 {"optionTwoSided", IDS_PRINT_PREVIEW_OPTION_TWO_SIDED},
243 {"optionsLabel", IDS_PRINT_PREVIEW_OPTIONS_LABEL},
244 {"pageRangeLimitInstructionWithValue",
245 IDS_PRINT_PREVIEW_PAGE_RANGE_LIMIT_INSTRUCTION_WITH_VALUE},
246 {"pageRangeSyntaxInstruction",
247 IDS_PRINT_PREVIEW_PAGE_RANGE_SYNTAX_INSTRUCTION},
248 {"pagesLabel", IDS_PRINT_PREVIEW_PAGES_LABEL},
249 {"pagesPerSheetLabel", IDS_PRINT_PREVIEW_PAGES_PER_SHEET_LABEL},
250 {"previewFailed", IDS_PRINT_PREVIEW_FAILED},
251 {"printOnBothSidesLabel", IDS_PRINT_PREVIEW_PRINT_ON_BOTH_SIDES_LABEL},
252 {"printButton", IDS_PRINT_PREVIEW_PRINT_BUTTON},
253 {"printDestinationsTitle", IDS_PRINT_PREVIEW_PRINT_DESTINATIONS_TITLE},
254 {"printPagesLabel", IDS_PRINT_PREVIEW_PRINT_PAGES_LABEL},
255 {"printPreviewPageLabelPlural", IDS_PRINT_PREVIEW_PAGE_LABEL_PLURAL},
256 {"printPreviewPageLabelSingular", IDS_PRINT_PREVIEW_PAGE_LABEL_SINGULAR},
257 {"printPreviewSheetsLabelPlural", IDS_PRINT_PREVIEW_SHEETS_LABEL_PLURAL},
258 {"printPreviewSheetsLabelSingular",
259 IDS_PRINT_PREVIEW_SHEETS_LABEL_SINGULAR},
260 {"printPreviewSummaryFormatShort", IDS_PRINT_PREVIEW_SUMMARY_FORMAT_SHORT},
261 {"printToGoogleDrive", IDS_PRINT_PREVIEW_PRINT_TO_GOOGLE_DRIVE},
262 {"printToPDF", IDS_PRINT_PREVIEW_PRINT_TO_PDF},
263 {"printerSharingInviteText", IDS_PRINT_PREVIEW_INVITE_TEXT},
264 {"printing", IDS_PRINT_PREVIEW_PRINTING},
265 {"recentDestinationsTitle", IDS_PRINT_PREVIEW_RECENT_DESTINATIONS_TITLE},
266 {"registerPrinterInformationMessage",
267 IDS_CLOUD_PRINT_REGISTER_PRINTER_INFORMATION},
268 {"reject", IDS_PRINT_PREVIEW_REJECT_INVITE},
269 {"resolveExtensionUSBDialogTitle",
270 IDS_PRINT_PREVIEW_RESOLVE_EXTENSION_USB_DIALOG_TITLE},
271 {"resolveExtensionUSBErrorMessage",
272 IDS_PRINT_PREVIEW_RESOLVE_EXTENSION_USB_ERROR_MESSAGE},
273 {"resolveExtensionUSBPermissionMessage",
274 IDS_PRINT_PREVIEW_RESOLVE_EXTENSION_USB_PERMISSION_MESSAGE},
275 {"right", IDS_PRINT_PREVIEW_RIGHT_MARGIN_LABEL},
276 {"saveButton", IDS_PRINT_PREVIEW_SAVE_BUTTON},
277 {"saving", IDS_PRINT_PREVIEW_SAVING},
278 {"scalingInstruction", IDS_PRINT_PREVIEW_SCALING_INSTRUCTION},
279 {"scalingLabel", IDS_PRINT_PREVIEW_SCALING_LABEL},
280 {"searchBoxPlaceholder", IDS_PRINT_PREVIEW_SEARCH_BOX_PLACEHOLDER},
281 {"searchResultBubbleText", IDS_SEARCH_RESULT_BUBBLE_TEXT},
282 {"searchResultsBubbleText", IDS_SEARCH_RESULTS_BUBBLE_TEXT},
283 {"selectButton", IDS_PRINT_PREVIEW_BUTTON_SELECT},
284 {"seeMore", IDS_PRINT_PREVIEW_SEE_MORE},
285 {"seeMoreDestinationsLabel", IDS_PRINT_PREVIEW_SEE_MORE_DESTINATIONS_LABEL},
286 {"title", IDS_PRINT_PREVIEW_TITLE},
287 {"top", IDS_PRINT_PREVIEW_TOP_MARGIN_LABEL},
288 {"unsupportedCloudPrinter", IDS_PRINT_PREVIEW_UNSUPPORTED_CLOUD_PRINTER},
289 #if defined(OS_CHROMEOS)
290 {"configuringFailedText", IDS_PRINT_CONFIGURING_FAILED_TEXT},
291 {"configuringInProgressText", IDS_PRINT_CONFIGURING_IN_PROGRESS_TEXT},
292 {"optionPin", IDS_PRINT_PREVIEW_OPTION_PIN},
293 {"pinErrorMessage", IDS_PRINT_PREVIEW_PIN_ERROR_MESSAGE},
294 {"pinPlaceholder", IDS_PRINT_PREVIEW_PIN_PLACEHOLDER},
295 {"printerEulaURL", IDS_PRINT_PREVIEW_EULA_URL},
296 #endif
297 #if defined(OS_MACOSX)
298 {"openPdfInPreviewOption", IDS_PRINT_PREVIEW_OPEN_PDF_IN_PREVIEW_APP},
299 {"openingPDFInPreview", IDS_PRINT_PREVIEW_OPENING_PDF_IN_PREVIEW_APP},
300 #endif
301 };
302 AddLocalizedStringsBulk(source, kLocalizedStrings);
303
304 source->AddString("gcpCertificateErrorLearnMoreURL",
305 chrome::kCloudPrintCertificateErrorLearnMoreURL);
306
307 #if !defined(OS_CHROMEOS)
308 const base::string16 shortcut_text(base::UTF8ToUTF16(kBasicPrintShortcut));
309 source->AddString("systemDialogOption",
310 l10n_util::GetStringFUTF16(
311 IDS_PRINT_PREVIEW_SYSTEM_DIALOG_OPTION, shortcut_text));
312 #endif
313 }
314
AddPrintPreviewFlags(content::WebUIDataSource * source,Profile * profile)315 void AddPrintPreviewFlags(content::WebUIDataSource* source, Profile* profile) {
316 #if defined(OS_CHROMEOS)
317 source->AddBoolean("useSystemDefaultPrinter", false);
318 #else
319 bool system_default_printer = profile->GetPrefs()->GetBoolean(
320 prefs::kPrintPreviewUseSystemDefaultPrinter);
321 source->AddBoolean("useSystemDefaultPrinter", system_default_printer);
322 #endif
323
324 bool enterprise_managed = false;
325 #if defined(OS_CHROMEOS)
326 policy::BrowserPolicyConnectorChromeOS* connector =
327 g_browser_process->platform_part()->browser_policy_connector_chromeos();
328 enterprise_managed = connector->IsEnterpriseManaged();
329 #elif defined(OS_WIN)
330 enterprise_managed = base::IsMachineExternallyManaged();
331 #endif
332 source->AddBoolean("isEnterpriseManaged", enterprise_managed);
333
334 bool cloud_printer_handler_enabled =
335 base::FeatureList::IsEnabled(features::kCloudPrinterHandler);
336 source->AddBoolean("cloudPrinterHandlerEnabled",
337 cloud_printer_handler_enabled);
338 }
339
SetupPrintPreviewPlugin(content::WebUIDataSource * source)340 void SetupPrintPreviewPlugin(content::WebUIDataSource* source) {
341 static constexpr struct {
342 const char* path;
343 int id;
344 } kPdfResources[] = {
345 {"pdf/browser_api.js", IDR_PDF_BROWSER_API_JS},
346 {"pdf/constants.js", IDR_PDF_CONSTANTS_JS},
347 {"pdf/controller.js", IDR_PDF_CONTROLLER_JS},
348 {"pdf/elements/icons.js", IDR_PDF_ICONS_JS},
349 {"pdf/elements/shared-vars.js", IDR_PDF_SHARED_VARS_JS},
350 {"pdf/elements/viewer-bookmark.js", IDR_PDF_VIEWER_BOOKMARK_JS},
351 {"pdf/elements/viewer-error-screen.js", IDR_PDF_VIEWER_ERROR_SCREEN_JS},
352 #if defined(OS_CHROMEOS)
353 {"pdf/elements/viewer-ink-host.js", IDR_PDF_VIEWER_INK_HOST_JS},
354 #endif
355 {"pdf/elements/viewer-page-indicator.js", IDR_PDF_VIEWER_PAGE_INDICATOR_JS},
356 {"pdf/elements/viewer-page-selector.js", IDR_PDF_VIEWER_PAGE_SELECTOR_JS},
357 {"pdf/elements/viewer-password-screen.js",
358 IDR_PDF_VIEWER_PASSWORD_SCREEN_JS},
359 {"pdf/elements/viewer-pdf-toolbar.js", IDR_PDF_VIEWER_PDF_TOOLBAR_JS},
360 #if defined(OS_CHROMEOS)
361 {"pdf/elements/viewer-form-warning.js", IDR_PDF_VIEWER_FORM_WARNING_JS},
362 {"pdf/elements/viewer-pen-options.js", IDR_PDF_VIEWER_PEN_OPTIONS_JS},
363 #endif
364 {"pdf/elements/viewer-toolbar-dropdown.js",
365 IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS},
366 {"pdf/elements/viewer-zoom-button.js", IDR_PDF_VIEWER_ZOOM_BUTTON_JS},
367 {"pdf/elements/viewer-zoom-toolbar.js", IDR_PDF_VIEWER_ZOOM_SELECTOR_JS},
368 {"pdf/gesture_detector.js", IDR_PDF_GESTURE_DETECTOR_JS},
369 {"pdf/index.css", IDR_PDF_INDEX_CSS},
370 {"pdf/index.html", IDR_PDF_INDEX_HTML},
371 {"pdf/main.js", IDR_PDF_MAIN_JS},
372 {"pdf/metrics.js", IDR_PDF_METRICS_JS},
373 {"pdf/navigator.js", IDR_PDF_NAVIGATOR_JS},
374 {"pdf/open_pdf_params_parser.js", IDR_PDF_OPEN_PDF_PARAMS_PARSER_JS},
375 {"pdf/pdf_scripting_api.js", IDR_PDF_PDF_SCRIPTING_API_JS},
376 {"pdf/pdf_viewer.js", IDR_PDF_PDF_VIEWER_JS},
377 {"pdf/toolbar_manager.js", IDR_PDF_TOOLBAR_MANAGER_JS},
378 {"pdf/viewport.js", IDR_PDF_VIEWPORT_JS},
379 {"pdf/viewport_scroller.js", IDR_PDF_VIEWPORT_SCROLLER_JS},
380 {"pdf/zoom_manager.js", IDR_PDF_ZOOM_MANAGER_JS},
381 };
382 for (const auto& resource : kPdfResources) {
383 source->AddResourcePath(resource.path, resource.id);
384 }
385
386 source->SetRequestFilter(base::BindRepeating(&ShouldHandleRequestCallback),
387 base::BindRepeating(&HandleRequestCallback));
388 source->OverrideContentSecurityPolicyChildSrc("child-src 'self';");
389 source->DisableDenyXFrameOptions();
390 source->OverrideContentSecurityPolicyObjectSrc("object-src 'self';");
391 }
392
CreatePrintPreviewUISource(Profile * profile)393 content::WebUIDataSource* CreatePrintPreviewUISource(Profile* profile) {
394 content::WebUIDataSource* source =
395 content::WebUIDataSource::Create(chrome::kChromeUIPrintHost);
396 #if BUILDFLAG(OPTIMIZE_WEBUI)
397 webui::SetupBundledWebUIDataSource(source, "print_preview.js",
398 IDR_PRINT_PREVIEW_PRINT_PREVIEW_ROLLUP_JS,
399 IDR_PRINT_PREVIEW_PRINT_PREVIEW_HTML);
400 #else
401 webui::SetupWebUIDataSource(
402 source,
403 base::make_span(kPrintPreviewResources, kPrintPreviewResourcesSize),
404 kGeneratedPath, IDR_PRINT_PREVIEW_PRINT_PREVIEW_HTML);
405 #endif
406 AddPrintPreviewStrings(source);
407 SetupPrintPreviewPlugin(source);
408 AddPrintPreviewFlags(source, profile);
409 return source;
410 }
411
CreatePrintPreviewHandlers(content::WebUI * web_ui)412 PrintPreviewHandler* CreatePrintPreviewHandlers(content::WebUI* web_ui) {
413 auto handler = std::make_unique<PrintPreviewHandler>();
414 PrintPreviewHandler* handler_ptr = handler.get();
415 web_ui->AddMessageHandler(std::move(handler));
416 web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
417 return handler_ptr;
418 }
419
420 } // namespace
421
PrintPreviewUI(content::WebUI * web_ui,std::unique_ptr<PrintPreviewHandler> handler)422 PrintPreviewUI::PrintPreviewUI(content::WebUI* web_ui,
423 std::unique_ptr<PrintPreviewHandler> handler)
424 : ConstrainedWebDialogUI(web_ui),
425 initial_preview_start_time_(base::TimeTicks::Now()),
426 handler_(handler.get()) {
427 web_ui->AddMessageHandler(std::move(handler));
428 }
429
PrintPreviewUI(content::WebUI * web_ui)430 PrintPreviewUI::PrintPreviewUI(content::WebUI* web_ui)
431 : ConstrainedWebDialogUI(web_ui),
432 initial_preview_start_time_(base::TimeTicks::Now()),
433 handler_(CreatePrintPreviewHandlers(web_ui)) {
434 // Set up the chrome://print/ data source.
435 Profile* profile = Profile::FromWebUI(web_ui);
436 content::WebUIDataSource* source = CreatePrintPreviewUISource(profile);
437 #if !BUILDFLAG(OPTIMIZE_WEBUI)
438 // For the Polymer 3 demo page.
439 ManagedUIHandler::Initialize(web_ui, source);
440 #endif
441 content::WebUIDataSource::Add(profile, source);
442
443 // Set up the chrome://theme/ source.
444 content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));
445 }
446
~PrintPreviewUI()447 PrintPreviewUI::~PrintPreviewUI() {
448 ClearPreviewUIId();
449 }
450
ClearPreviewUIId()451 void PrintPreviewUI::ClearPreviewUIId() {
452 if (!id_)
453 return;
454
455 PrintPreviewDataService::GetInstance()->RemoveEntry(*id_);
456 g_print_preview_request_id_map.Get().Erase(*id_);
457 g_print_preview_ui_id_map.Get().Remove(*id_);
458 id_.reset();
459 }
460
GetPrintPreviewDataForIndex(int index,scoped_refptr<base::RefCountedMemory> * data) const461 void PrintPreviewUI::GetPrintPreviewDataForIndex(
462 int index,
463 scoped_refptr<base::RefCountedMemory>* data) const {
464 PrintPreviewDataService::GetInstance()->GetDataEntry(*id_, index, data);
465 }
466
SetPrintPreviewDataForIndex(int index,scoped_refptr<base::RefCountedMemory> data)467 void PrintPreviewUI::SetPrintPreviewDataForIndex(
468 int index,
469 scoped_refptr<base::RefCountedMemory> data) {
470 PrintPreviewDataService::GetInstance()->SetDataEntry(*id_, index,
471 std::move(data));
472 }
473
474 // static
ParseDataPath(const std::string & path,int * ui_id,int * page_index)475 bool PrintPreviewUI::ParseDataPath(const std::string& path,
476 int* ui_id,
477 int* page_index) {
478 std::string file_path = path.substr(0, path.find_first_of('?'));
479 if (!base::EndsWith(file_path, "/print.pdf", base::CompareCase::SENSITIVE))
480 return false;
481
482 std::vector<std::string> url_substr =
483 base::SplitString(path, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
484 if (url_substr.size() != 3)
485 return false;
486
487 int preview_ui_id = -1;
488 if (!base::StringToInt(url_substr[0], &preview_ui_id) || preview_ui_id < 0)
489 return false;
490
491 int preview_page_index = 0;
492 if (!base::StringToInt(url_substr[1], &preview_page_index))
493 return false;
494
495 if (ui_id)
496 *ui_id = preview_ui_id;
497 if (page_index)
498 *page_index = preview_page_index;
499 return true;
500 }
501
ClearAllPreviewData()502 void PrintPreviewUI::ClearAllPreviewData() {
503 PrintPreviewDataService::GetInstance()->RemoveEntry(*id_);
504 }
505
SetInitiatorTitle(const base::string16 & job_title)506 void PrintPreviewUI::SetInitiatorTitle(
507 const base::string16& job_title) {
508 initiator_title_ = job_title;
509 }
510
LastPageComposited(int page_number) const511 bool PrintPreviewUI::LastPageComposited(int page_number) const {
512 if (pages_to_render_.empty())
513 return false;
514
515 return page_number == pages_to_render_.back();
516 }
517
GetPageToNupConvertIndex(int page_number) const518 int PrintPreviewUI::GetPageToNupConvertIndex(int page_number) const {
519 for (size_t index = 0; index < pages_to_render_.size(); ++index) {
520 if (page_number == pages_to_render_[index])
521 return index;
522 }
523 return -1;
524 }
525
526 std::vector<base::ReadOnlySharedMemoryRegion>
TakePagesForNupConvert()527 PrintPreviewUI::TakePagesForNupConvert() {
528 return std::move(pages_for_nup_convert_);
529 }
530
AddPdfPageForNupConversion(base::ReadOnlySharedMemoryRegion pdf_page)531 void PrintPreviewUI::AddPdfPageForNupConversion(
532 base::ReadOnlySharedMemoryRegion pdf_page) {
533 pages_for_nup_convert_.push_back(std::move(pdf_page));
534 }
535
536 // static
SetInitialParams(content::WebContents * print_preview_dialog,const PrintHostMsg_RequestPrintPreview_Params & params)537 void PrintPreviewUI::SetInitialParams(
538 content::WebContents* print_preview_dialog,
539 const PrintHostMsg_RequestPrintPreview_Params& params) {
540 if (!print_preview_dialog || !print_preview_dialog->GetWebUI())
541 return;
542 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
543 print_preview_dialog->GetWebUI()->GetController());
544 print_preview_ui->source_is_arc_ = params.is_from_arc;
545 print_preview_ui->source_is_modifiable_ = params.is_modifiable;
546 print_preview_ui->source_is_pdf_ = params.is_pdf;
547 print_preview_ui->source_has_selection_ = params.has_selection;
548 print_preview_ui->print_selection_only_ = params.selection_only;
549 }
550
551 // static
ShouldCancelRequest(const PrintHostMsg_PreviewIds & ids)552 bool PrintPreviewUI::ShouldCancelRequest(const PrintHostMsg_PreviewIds& ids) {
553 int current_id = -1;
554 if (!g_print_preview_request_id_map.Get().Get(ids.ui_id, ¤t_id))
555 return true;
556 return ids.request_id != current_id;
557 }
558
GetIDForPrintPreviewUI() const559 base::Optional<int32_t> PrintPreviewUI::GetIDForPrintPreviewUI() const {
560 return id_;
561 }
562
OnPrintPreviewDialogClosed()563 void PrintPreviewUI::OnPrintPreviewDialogClosed() {
564 WebContents* preview_dialog = web_ui()->GetWebContents();
565 BackgroundPrintingManager* background_printing_manager =
566 g_browser_process->background_printing_manager();
567 if (background_printing_manager->HasPrintPreviewDialog(preview_dialog))
568 return;
569 OnClosePrintPreviewDialog();
570 }
571
OnInitiatorClosed()572 void PrintPreviewUI::OnInitiatorClosed() {
573 // Should only get here if the initiator was still tracked by the Print
574 // Preview Dialog Controller, so the print job has not yet been sent.
575 WebContents* preview_dialog = web_ui()->GetWebContents();
576 BackgroundPrintingManager* background_printing_manager =
577 g_browser_process->background_printing_manager();
578 if (background_printing_manager->HasPrintPreviewDialog(preview_dialog)) {
579 // Dialog is hidden but is still generating the preview. Cancel the print
580 // request as it can't be completed.
581 background_printing_manager->OnPrintRequestCancelled(preview_dialog);
582 handler_->OnPrintRequestCancelled();
583 } else {
584 // Initiator was closed while print preview dialog was still open.
585 OnClosePrintPreviewDialog();
586 }
587 }
588
OnPrintPreviewCancelled(int request_id)589 void PrintPreviewUI::OnPrintPreviewCancelled(int request_id) {
590 handler_->OnPrintPreviewCancelled(request_id);
591 }
592
OnPrintPreviewRequest(int request_id)593 void PrintPreviewUI::OnPrintPreviewRequest(int request_id) {
594 if (!initial_preview_start_time_.is_null()) {
595 base::UmaHistogramTimes(
596 "PrintPreview.InitializationTime",
597 base::TimeTicks::Now() - initial_preview_start_time_);
598 }
599 g_print_preview_request_id_map.Get().Set(*id_, request_id);
600 }
601
OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params & params,int request_id)602 void PrintPreviewUI::OnDidStartPreview(
603 const PrintHostMsg_DidStartPreview_Params& params,
604 int request_id) {
605 DCHECK_GT(params.page_count, 0);
606 DCHECK(!params.pages_to_render.empty());
607
608 pages_to_render_ = params.pages_to_render;
609 pages_to_render_index_ = 0;
610 pages_per_sheet_ = params.pages_per_sheet;
611 page_size_ = params.page_size;
612 ClearAllPreviewData();
613
614 if (g_test_delegate)
615 g_test_delegate->DidGetPreviewPageCount(params.page_count);
616 handler_->SendPageCountReady(params.page_count, params.fit_to_page_scaling,
617 request_id);
618 }
619
OnDidGetDefaultPageLayout(const PageSizeMargins & page_layout,const gfx::Rect & printable_area,bool has_custom_page_size_style,int request_id)620 void PrintPreviewUI::OnDidGetDefaultPageLayout(
621 const PageSizeMargins& page_layout,
622 const gfx::Rect& printable_area,
623 bool has_custom_page_size_style,
624 int request_id) {
625 if (page_layout.margin_top < 0 || page_layout.margin_left < 0 ||
626 page_layout.margin_bottom < 0 || page_layout.margin_right < 0 ||
627 page_layout.content_width < 0 || page_layout.content_height < 0 ||
628 printable_area.width() <= 0 || printable_area.height() <= 0) {
629 NOTREACHED();
630 return;
631 }
632 // Save printable_area information for N-up conversion.
633 printable_area_ = printable_area;
634
635 base::DictionaryValue layout;
636 layout.SetDouble(kSettingMarginTop, page_layout.margin_top);
637 layout.SetDouble(kSettingMarginLeft, page_layout.margin_left);
638 layout.SetDouble(kSettingMarginBottom, page_layout.margin_bottom);
639 layout.SetDouble(kSettingMarginRight, page_layout.margin_right);
640 layout.SetDouble(kSettingContentWidth, page_layout.content_width);
641 layout.SetDouble(kSettingContentHeight, page_layout.content_height);
642 layout.SetInteger(kSettingPrintableAreaX, printable_area.x());
643 layout.SetInteger(kSettingPrintableAreaY, printable_area.y());
644 layout.SetInteger(kSettingPrintableAreaWidth, printable_area.width());
645 layout.SetInteger(kSettingPrintableAreaHeight, printable_area.height());
646 handler_->SendPageLayoutReady(layout, has_custom_page_size_style, request_id);
647 }
648
OnPendingPreviewPage(int page_number)649 bool PrintPreviewUI::OnPendingPreviewPage(int page_number) {
650 if (pages_to_render_index_ >= pages_to_render_.size())
651 return false;
652
653 bool matched = page_number == pages_to_render_[pages_to_render_index_];
654 ++pages_to_render_index_;
655 return matched;
656 }
657
OnDidPreviewPage(int page_number,scoped_refptr<base::RefCountedMemory> data,int preview_request_id)658 void PrintPreviewUI::OnDidPreviewPage(
659 int page_number,
660 scoped_refptr<base::RefCountedMemory> data,
661 int preview_request_id) {
662 DCHECK_GE(page_number, 0);
663
664 SetPrintPreviewDataForIndex(page_number, std::move(data));
665
666 if (g_test_delegate)
667 g_test_delegate->DidRenderPreviewPage(web_ui()->GetWebContents());
668 handler_->SendPagePreviewReady(page_number, *id_, preview_request_id);
669 }
670
OnPreviewDataIsAvailable(scoped_refptr<base::RefCountedMemory> data,int preview_request_id)671 void PrintPreviewUI::OnPreviewDataIsAvailable(
672 scoped_refptr<base::RefCountedMemory> data,
673 int preview_request_id) {
674 if (!initial_preview_start_time_.is_null()) {
675 base::UmaHistogramTimes(
676 "PrintPreview.InitialDisplayTime",
677 base::TimeTicks::Now() - initial_preview_start_time_);
678 base::UmaHistogramCounts1M(
679 "PrintPreview.RegeneratePreviewRequest.BeforeFirstData",
680 handler_->regenerate_preview_request_count());
681 initial_preview_start_time_ = base::TimeTicks();
682 }
683
684 SetPrintPreviewDataForIndex(COMPLETE_PREVIEW_DOCUMENT_INDEX, std::move(data));
685
686 handler_->OnPrintPreviewReady(*id_, preview_request_id);
687 }
688
OnCancelPendingPreviewRequest()689 void PrintPreviewUI::OnCancelPendingPreviewRequest() {
690 g_print_preview_request_id_map.Get().Set(*id_, -1);
691 }
692
OnPrintPreviewFailed(int request_id)693 void PrintPreviewUI::OnPrintPreviewFailed(int request_id) {
694 handler_->OnPrintPreviewFailed(request_id);
695 }
696
OnInvalidPrinterSettings(int request_id)697 void PrintPreviewUI::OnInvalidPrinterSettings(int request_id) {
698 handler_->OnInvalidPrinterSettings(request_id);
699 }
700
OnHidePreviewDialog()701 void PrintPreviewUI::OnHidePreviewDialog() {
702 WebContents* preview_dialog = web_ui()->GetWebContents();
703 BackgroundPrintingManager* background_printing_manager =
704 g_browser_process->background_printing_manager();
705 if (background_printing_manager->HasPrintPreviewDialog(preview_dialog))
706 return;
707
708 ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
709 if (!delegate)
710 return;
711 std::unique_ptr<content::WebContents> preview_contents =
712 delegate->ReleaseWebContents();
713 DCHECK_EQ(preview_dialog, preview_contents.get());
714 background_printing_manager->OwnPrintPreviewDialog(
715 std::move(preview_contents));
716 OnClosePrintPreviewDialog();
717 }
718
OnClosePrintPreviewDialog()719 void PrintPreviewUI::OnClosePrintPreviewDialog() {
720 if (dialog_closed_)
721 return;
722 dialog_closed_ = true;
723 ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
724 if (!delegate)
725 return;
726 delegate->GetWebDialogDelegate()->OnDialogClosed(std::string());
727 delegate->OnDialogCloseFromWebUI();
728 }
729
OnSetOptionsFromDocument(const PrintHostMsg_SetOptionsFromDocument_Params & params,int request_id)730 void PrintPreviewUI::OnSetOptionsFromDocument(
731 const PrintHostMsg_SetOptionsFromDocument_Params& params,
732 int request_id) {
733 handler_->SendPrintPresetOptions(params.is_scaling_disabled, params.copies,
734 params.duplex, request_id);
735 }
736
737 // static
SetDelegateForTesting(TestDelegate * delegate)738 void PrintPreviewUI::SetDelegateForTesting(TestDelegate* delegate) {
739 g_test_delegate = delegate;
740 }
741
SetSelectedFileForTesting(const base::FilePath & path)742 void PrintPreviewUI::SetSelectedFileForTesting(const base::FilePath& path) {
743 handler_->FileSelectedForTesting(path, 0, nullptr);
744 }
745
SetPdfSavedClosureForTesting(base::OnceClosure closure)746 void PrintPreviewUI::SetPdfSavedClosureForTesting(base::OnceClosure closure) {
747 handler_->SetPdfSavedClosureForTesting(std::move(closure));
748 }
749
SendEnableManipulateSettingsForTest()750 void PrintPreviewUI::SendEnableManipulateSettingsForTest() {
751 handler_->SendEnableManipulateSettingsForTest();
752 }
753
SendManipulateSettingsForTest(const base::DictionaryValue & settings)754 void PrintPreviewUI::SendManipulateSettingsForTest(
755 const base::DictionaryValue& settings) {
756 handler_->SendManipulateSettingsForTest(settings);
757 }
758
SetPrintPreviewDataForIndexForTest(int index,scoped_refptr<base::RefCountedMemory> data)759 void PrintPreviewUI::SetPrintPreviewDataForIndexForTest(
760 int index,
761 scoped_refptr<base::RefCountedMemory> data) {
762 SetPrintPreviewDataForIndex(index, data);
763 }
764
ClearAllPreviewDataForTest()765 void PrintPreviewUI::ClearAllPreviewDataForTest() {
766 ClearAllPreviewData();
767 }
768
SetPreviewUIId()769 void PrintPreviewUI::SetPreviewUIId() {
770 DCHECK(!id_);
771 id_ = g_print_preview_ui_id_map.Get().Add(this);
772 g_print_preview_request_id_map.Get().Set(*id_, -1);
773 }
774
775 } // namespace printing
776