Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / preview_generator.js
blobeaba9e86a73bf5ac4da2105b14c84ead5db05713
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.
5 cr.define('print_preview', function() {
6   'use strict';
8   /**
9    * Interface to the Chromium print preview generator.
10    * @param {!print_preview.DestinationStore} destinationStore Used to get the
11    *     currently selected destination.
12    * @param {!print_preview.PrintTicketStore} printTicketStore Used to read the
13    *     state of the ticket and write document information.
14    * @param {!print_preview.NativeLayer} nativeLayer Used to communicate to
15    *     Chromium's preview rendering system.
16    * @param {!print_preview.DocumentInfo} documentInfo Document data model.
17    * @constructor
18    * @extends {cr.EventTarget}
19    */
20   function PreviewGenerator(
21        destinationStore, printTicketStore, nativeLayer, documentInfo) {
22     cr.EventTarget.call(this);
24     /**
25      * Used to get the currently selected destination.
26      * @type {!print_preview.DestinationStore}
27      * @private
28      */
29     this.destinationStore_ = destinationStore;
31     /**
32      * Used to read the state of the ticket and write document information.
33      * @type {!print_preview.PrintTicketStore}
34      * @private
35      */
36     this.printTicketStore_ = printTicketStore;
38     /**
39      * Interface to the Chromium native layer.
40      * @type {!print_preview.NativeLayer}
41      * @private
42      */
43     this.nativeLayer_ = nativeLayer;
45     /**
46      * Document data model.
47      * @type {!print_preview.DocumentInfo}
48      * @private
49      */
50     this.documentInfo_ = documentInfo;
52     /**
53      * ID of current in-flight request. Requests that do not share this ID will
54      * be ignored.
55      * @type {number}
56      * @private
57      */
58     this.inFlightRequestId_ = -1;
60     /**
61      * Media size to generate preview with. {@code null} indicates default size.
62      * @type {cp.cdd.MediaSizeTicketItem}
63      * @private
64      */
65     this.mediaSize_ = null;
67     /**
68      * Whether the previews are being generated in landscape mode.
69      * @type {boolean}
70      * @private
71      */
72     this.isLandscapeEnabled_ = false;
74     /**
75      * Whether the previews are being generated from a distilled page.
76      * @type {boolean}
77      * @private
78      */
79     this.isDistillPageEnabled_ = false;
81     /**
82      * Whether the previews are being generated with a header and footer.
83      * @type {boolean}
84      * @private
85      */
86     this.isHeaderFooterEnabled_ = false;
88     /**
89      * Whether the previews are being generated in color.
90      * @type {boolean}
91      * @private
92      */
93     this.colorValue_ = false;
95     /**
96      * Whether the document should be fitted to the page.
97      * @type {boolean}
98      * @private
99      */
100     this.isFitToPageEnabled_ = false;
102     /**
103      * Page ranges setting used used to generate the last preview.
104      * @type {!Array<object<{from: number, to: number}>>}
105      * @private
106      */
107     this.pageRanges_ = null;
109     /**
110      * Margins type used to generate the last preview.
111      * @type {!print_preview.ticket_items.MarginsType.Value}
112      * @private
113      */
114     this.marginsType_ = print_preview.ticket_items.MarginsType.Value.DEFAULT;
116     /**
117      * Whether the document should have element CSS backgrounds printed.
118      * @type {boolean}
119      * @private
120      */
121     this.isCssBackgroundEnabled_ = false;
123     /**
124      * Destination that was selected for the last preview.
125      * @type {print_preview.Destination}
126      * @private
127      */
128     this.selectedDestination_ = null;
130     /**
131      * Event tracker used to keep track of native layer events.
132      * @type {!EventTracker}
133      * @private
134      */
135     this.tracker_ = new EventTracker();
137     this.addEventListeners_();
138   };
140   /**
141    * Event types dispatched by the preview generator.
142    * @enum {string}
143    */
144   PreviewGenerator.EventType = {
145     // Dispatched when the document can be printed.
146     DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY',
148     // Dispatched when a page preview is ready. The previewIndex field of the
149     // event is the index of the page in the modified document, not the
150     // original. So page 4 of the original document might be previewIndex = 0 of
151     // the modified document.
152     PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY',
154     // Dispatched when the document preview starts to be generated.
155     PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START',
157     // Dispatched when the current print preview request fails.
158     FAIL: 'print_preview.PreviewGenerator.FAIL'
159   };
161   PreviewGenerator.prototype = {
162     __proto__: cr.EventTarget.prototype,
164     /**
165      * Request that new preview be generated. A preview request will not be
166      * generated if the print ticket has not changed sufficiently.
167      * @return {boolean} Whether a new preview was actually requested.
168      */
169     requestPreview: function() {
170       if (!this.printTicketStore_.isTicketValidForPreview() ||
171           !this.printTicketStore_.isInitialized ||
172           !this.destinationStore_.selectedDestination) {
173         return false;
174       }
175       if (!this.hasPreviewChanged_()) {
176         // Changes to these ticket items might not trigger a new preview, but
177         // they still need to be recorded.
178         this.marginsType_ = this.printTicketStore_.marginsType.getValue();
179         return false;
180       }
181       this.mediaSize_ = this.printTicketStore_.mediaSize.getValue();
182       this.isLandscapeEnabled_ = this.printTicketStore_.landscape.getValue();
183       this.isHeaderFooterEnabled_ =
184           this.printTicketStore_.headerFooter.getValue();
185       this.isDistillPageEnabled_ =
186           this.printTicketStore_.distillPage.getValue();
187       this.colorValue_ = this.printTicketStore_.color.getValue();
188       this.isFitToPageEnabled_ = this.printTicketStore_.fitToPage.getValue();
189       this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
190       this.marginsType_ = this.printTicketStore_.marginsType.getValue();
191       this.isCssBackgroundEnabled_ =
192           this.printTicketStore_.cssBackground.getValue();
193       this.isSelectionOnlyEnabled_ =
194           this.printTicketStore_.selectionOnly.getValue();
195       this.selectedDestination_ = this.destinationStore_.selectedDestination;
197       this.inFlightRequestId_++;
198       this.nativeLayer_.startGetPreview(
199           this.destinationStore_.selectedDestination,
200           this.printTicketStore_,
201           this.documentInfo_,
202           this.inFlightRequestId_);
203       return true;
204     },
206     /** Removes all event listeners that the preview generator has attached. */
207     removeEventListeners: function() {
208       this.tracker_.removeAll();
209     },
211     /**
212      * Adds event listeners to the relevant native layer events.
213      * @private
214      */
215     addEventListeners_: function() {
216       this.tracker_.add(
217           this.nativeLayer_,
218           print_preview.NativeLayer.EventType.PAGE_LAYOUT_READY,
219           this.onPageLayoutReady_.bind(this));
220       this.tracker_.add(
221           this.nativeLayer_,
222           print_preview.NativeLayer.EventType.PAGE_COUNT_READY,
223           this.onPageCountReady_.bind(this));
224       this.tracker_.add(
225           this.nativeLayer_,
226           print_preview.NativeLayer.EventType.PAGE_PREVIEW_READY,
227           this.onPagePreviewReady_.bind(this));
228       this.tracker_.add(
229           this.nativeLayer_,
230           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_DONE,
231           this.onPreviewGenerationDone_.bind(this));
232       this.tracker_.add(
233           this.nativeLayer_,
234           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_FAIL,
235           this.onPreviewGenerationFail_.bind(this));
236     },
238     /**
239      * Dispatches a PAGE_READY event to signal that a page preview is ready.
240      * @param {number} previewIndex Index of the page with respect to the pages
241      *     shown in the preview. E.g an index of 0 is the first displayed page,
242      *     but not necessarily the first original document page.
243      * @param {number} pageNumber Number of the page with respect to the
244      *     document. A value of 3 means it's the third page of the original
245      *     document.
246      * @param {number} previewUid Unique identifier of the preview.
247      * @private
248      */
249     dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) {
250       var pageGenEvent = new Event(PreviewGenerator.EventType.PAGE_READY);
251       pageGenEvent.previewIndex = previewIndex;
252       pageGenEvent.previewUrl = 'chrome://print/' + previewUid.toString() +
253           '/' + (pageNumber - 1) + '/print.pdf';
254       this.dispatchEvent(pageGenEvent);
255     },
257     /**
258      * Dispatches a PREVIEW_START event. Signals that the preview should be
259      * reloaded.
260      * @param {number} previewUid Unique identifier of the preview.
261      * @param {number} index Index of the first page of the preview.
262      * @private
263      */
264     dispatchPreviewStartEvent_: function(previewUid, index) {
265       var previewStartEvent = new Event(
266           PreviewGenerator.EventType.PREVIEW_START);
267       if (!this.documentInfo_.isModifiable) {
268         index = -1;
269       }
270       previewStartEvent.previewUrl = 'chrome://print/' +
271           previewUid.toString() + '/' + index + '/print.pdf';
272       this.dispatchEvent(previewStartEvent);
273     },
275     /**
276      * @return {boolean} Whether the print ticket has changed sufficiently to
277      *     determine whether a new preview request should be issued.
278      * @private
279      */
280     hasPreviewChanged_: function() {
281       var ticketStore = this.printTicketStore_;
282       return this.inFlightRequestId_ == -1 ||
283           !ticketStore.mediaSize.isValueEqual(this.mediaSize_) ||
284           !ticketStore.landscape.isValueEqual(this.isLandscapeEnabled_) ||
285           !ticketStore.headerFooter.isValueEqual(this.isHeaderFooterEnabled_) ||
286           !ticketStore.distillPage.isValueEqual(
287               this.isDistillPageEnabled_) ||
288           !ticketStore.color.isValueEqual(this.colorValue_) ||
289           !ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
290           this.pageRanges_ == null ||
291           !areRangesEqual(ticketStore.pageRange.getPageRanges(),
292                           this.pageRanges_) ||
293           (!ticketStore.marginsType.isValueEqual(this.marginsType_) &&
294               !ticketStore.marginsType.isValueEqual(
295                   print_preview.ticket_items.MarginsType.Value.CUSTOM)) ||
296           (ticketStore.marginsType.isValueEqual(
297               print_preview.ticket_items.MarginsType.Value.CUSTOM) &&
298               !ticketStore.customMargins.isValueEqual(
299                   this.documentInfo_.margins)) ||
300           !ticketStore.cssBackground.isValueEqual(
301               this.isCssBackgroundEnabled_) ||
302           !ticketStore.selectionOnly.isValueEqual(
303               this.isSelectionOnlyEnabled_) ||
304           (this.selectedDestination_ !=
305               this.destinationStore_.selectedDestination);
306     },
308     /**
309      * Called when the page layout of the document is ready. Always occurs
310      * as a result of a preview request.
311      * @param {Event} event Contains layout info about the document.
312      * @private
313      */
314     onPageLayoutReady_: function(event) {
315       // NOTE: A request ID is not specified, so assuming its for the current
316       // in-flight request.
318       var origin = new print_preview.Coordinate2d(
319           event.pageLayout.printableAreaX,
320           event.pageLayout.printableAreaY);
321       var size = new print_preview.Size(
322           event.pageLayout.printableAreaWidth,
323           event.pageLayout.printableAreaHeight);
325       var margins = new print_preview.Margins(
326           Math.round(event.pageLayout.marginTop),
327           Math.round(event.pageLayout.marginRight),
328           Math.round(event.pageLayout.marginBottom),
329           Math.round(event.pageLayout.marginLeft));
331       var o = print_preview.ticket_items.CustomMargins.Orientation;
332       var pageSize = new print_preview.Size(
333           event.pageLayout.contentWidth +
334               margins.get(o.LEFT) + margins.get(o.RIGHT),
335           event.pageLayout.contentHeight +
336               margins.get(o.TOP) + margins.get(o.BOTTOM));
338       this.documentInfo_.updatePageInfo(
339           new print_preview.PrintableArea(origin, size),
340           pageSize,
341           event.hasCustomPageSizeStyle,
342           margins);
343     },
345     /**
346      * Called when the document page count is received from the native layer.
347      * Always occurs as a result of a preview request.
348      * @param {Event} event Contains the document's page count.
349      * @private
350      */
351     onPageCountReady_: function(event) {
352       if (this.inFlightRequestId_ != event.previewResponseId) {
353         return; // Ignore old response.
354       }
355       this.documentInfo_.updatePageCount(event.pageCount);
356       this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
357     },
359     /**
360      * Called when a page's preview has been generated. Dispatches a
361      * PAGE_READY event.
362      * @param {Event} event Contains the page index and preview UID.
363      * @private
364      */
365     onPagePreviewReady_: function(event) {
366       if (this.inFlightRequestId_ != event.previewResponseId) {
367         return; // Ignore old response.
368       }
369       var pageNumber = event.pageIndex + 1;
370       var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet();
371       if (pageNumberSet.hasPageNumber(pageNumber)) {
372         var previewIndex = pageNumberSet.getPageNumberIndex(pageNumber);
373         if (previewIndex == 0) {
374           this.dispatchPreviewStartEvent_(event.previewUid, event.pageIndex);
375         }
376         this.dispatchPageReadyEvent_(
377             previewIndex, pageNumber, event.previewUid);
378       }
379     },
381     /**
382      * Called when the preview generation is complete. Dispatches a
383      * DOCUMENT_READY event.
384      * @param {Event} event Contains the preview UID and response ID.
385      * @private
386      */
387     onPreviewGenerationDone_: function(event) {
388       if (this.inFlightRequestId_ != event.previewResponseId) {
389         return; // Ignore old response.
390       }
391       // Dispatch a PREVIEW_START event since non-modifiable documents don't
392       // trigger PAGE_READY events.
393       if (!this.documentInfo_.isModifiable) {
394         this.dispatchPreviewStartEvent_(event.previewUid, 0);
395       }
396       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
397     },
399     /**
400      * Called when the preview generation fails.
401      * @private
402      */
403     onPreviewGenerationFail_: function() {
404       // NOTE: No request ID is returned from Chromium so its assumed its the
405       // current one.
406       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL);
407     }
408   };
410   // Export
411   return {
412     PreviewGenerator: PreviewGenerator
413   };