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, &current_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