Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / preview_generator.js
bloba26345ca87782d7fd57c9016b470e204c854cae3
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      * Whether the previews are being generated in landscape mode.
62      * @type {boolean}
63      * @private
64      */
65     this.isLandscapeEnabled_ = false;
67     /**
68      * Whether the previews are being generated with a header and footer.
69      * @type {boolean}
70      * @private
71      */
72     this.isHeaderFooterEnabled_ = false;
74     /**
75      * Whether the previews are being generated in color.
76      * @type {boolean}
77      * @private
78      */
79     this.colorValue_ = false;
81     /**
82      * Whether the document should be fitted to the page.
83      * @type {boolean}
84      * @private
85      */
86     this.isFitToPageEnabled_ = false;
88     /**
89      * Page ranges setting used used to generate the last preview.
90      * @type {!Array.<object.<{from: number, to: number}>>}
91      * @private
92      */
93     this.pageRanges_ = null;
95     /**
96      * Margins type used to generate the last preview.
97      * @type {!print_preview.ticket_items.MarginsType.Value}
98      * @private
99      */
100     this.marginsType_ = print_preview.ticket_items.MarginsType.Value.DEFAULT;
102     /**
103      * Whether the document should have element CSS backgrounds printed.
104      * @type {boolean}
105      * @private
106      */
107     this.isCssBackgroundEnabled_ = false;
109     /**
110      * Destination that was selected for the last preview.
111      * @type {print_preview.Destination}
112      * @private
113      */
114     this.selectedDestination_ = null;
116     /**
117      * Event tracker used to keep track of native layer events.
118      * @type {!EventTracker}
119      * @private
120      */
121     this.tracker_ = new EventTracker();
123     this.addEventListeners_();
124   };
126   /**
127    * Event types dispatched by the preview generator.
128    * @enum {string}
129    */
130   PreviewGenerator.EventType = {
131     // Dispatched when the document can be printed.
132     DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY',
134     // Dispatched when a page preview is ready. The previewIndex field of the
135     // event is the index of the page in the modified document, not the
136     // original. So page 4 of the original document might be previewIndex = 0 of
137     // the modified document.
138     PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY',
140     // Dispatched when the document preview starts to be generated.
141     PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START',
143     // Dispatched when the current print preview request fails.
144     FAIL: 'print_preview.PreviewGenerator.FAIL'
145   };
147   PreviewGenerator.prototype = {
148     __proto__: cr.EventTarget.prototype,
150     /**
151      * Request that new preview be generated. A preview request will not be
152      * generated if the print ticket has not changed sufficiently.
153      * @return {boolean} Whether a new preview was actually requested.
154      */
155     requestPreview: function() {
156       if (!this.printTicketStore_.isTicketValidForPreview() ||
157           !this.printTicketStore_.isInitialized) {
158         return false;
159       }
160       if (!this.hasPreviewChanged_()) {
161         // Changes to these ticket items might not trigger a new preview, but
162         // they still need to be recorded.
163         this.marginsType_ = this.printTicketStore_.marginsType.getValue();
164         return false;
165       }
166       this.isLandscapeEnabled_ = this.printTicketStore_.landscape.getValue();
167       this.isHeaderFooterEnabled_ =
168           this.printTicketStore_.headerFooter.getValue();
169       this.colorValue_ = this.printTicketStore_.color.getValue();
170       this.isFitToPageEnabled_ = this.printTicketStore_.fitToPage.getValue();
171       this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
172       this.marginsType_ = this.printTicketStore_.marginsType.getValue();
173       this.isCssBackgroundEnabled_ =
174           this.printTicketStore_.cssBackground.getValue();
175       this.isSelectionOnlyEnabled_ =
176           this.printTicketStore_.selectionOnly.getValue();
177       this.selectedDestination_ = this.destinationStore_.selectedDestination;
179       this.inFlightRequestId_++;
180       this.nativeLayer_.startGetPreview(
181           this.destinationStore_.selectedDestination,
182           this.printTicketStore_,
183           this.documentInfo_,
184           this.inFlightRequestId_);
185       return true;
186     },
188     /** Removes all event listeners that the preview generator has attached. */
189     removeEventListeners: function() {
190       this.tracker_.removeAll();
191     },
193     /**
194      * Adds event listeners to the relevant native layer events.
195      * @private
196      */
197     addEventListeners_: function() {
198       this.tracker_.add(
199           this.nativeLayer_,
200           print_preview.NativeLayer.EventType.PAGE_LAYOUT_READY,
201           this.onPageLayoutReady_.bind(this));
202       this.tracker_.add(
203           this.nativeLayer_,
204           print_preview.NativeLayer.EventType.PAGE_COUNT_READY,
205           this.onPageCountReady_.bind(this));
206       this.tracker_.add(
207           this.nativeLayer_,
208           print_preview.NativeLayer.EventType.PAGE_PREVIEW_READY,
209           this.onPagePreviewReady_.bind(this));
210       this.tracker_.add(
211           this.nativeLayer_,
212           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_DONE,
213           this.onPreviewGenerationDone_.bind(this));
214       this.tracker_.add(
215           this.nativeLayer_,
216           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_FAIL,
217           this.onPreviewGenerationFail_.bind(this));
218     },
220     /**
221      * Dispatches a PAGE_READY event to signal that a page preview is ready.
222      * @param {number} previewIndex Index of the page with respect to the pages
223      *     shown in the preview. E.g an index of 0 is the first displayed page,
224      *     but not necessarily the first original document page.
225      * @param {number} pageNumber Number of the page with respect to the
226      *     document. A value of 3 means it's the third page of the original
227      *     document.
228      * @param {number} previewUid Unique identifier of the preview.
229      * @private
230      */
231     dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) {
232       var pageGenEvent = new Event(PreviewGenerator.EventType.PAGE_READY);
233       pageGenEvent.previewIndex = previewIndex;
234       pageGenEvent.previewUrl = 'chrome://print/' + previewUid.toString() +
235           '/' + (pageNumber - 1) + '/print.pdf';
236       this.dispatchEvent(pageGenEvent);
237     },
239     /**
240      * Dispatches a PREVIEW_START event. Signals that the preview should be
241      * reloaded.
242      * @param {number} previewUid Unique identifier of the preview.
243      * @param {number} index Index of the first page of the preview.
244      * @private
245      */
246     dispatchPreviewStartEvent_: function(previewUid, index) {
247       var previewStartEvent = new Event(
248           PreviewGenerator.EventType.PREVIEW_START);
249       if (!this.documentInfo_.isModifiable) {
250         index = -1;
251       }
252       previewStartEvent.previewUrl = 'chrome://print/' +
253           previewUid.toString() + '/' + index + '/print.pdf';
254       this.dispatchEvent(previewStartEvent);
255     },
257     /**
258      * @return {boolean} Whether the print ticket has changed sufficiently to
259      *     determine whether a new preview request should be issued.
260      * @private
261      */
262     hasPreviewChanged_: function() {
263       var ticketStore = this.printTicketStore_;
264       return this.inFlightRequestId_ == -1 ||
265           !ticketStore.landscape.isValueEqual(this.isLandscapeEnabled_) ||
266           !ticketStore.headerFooter.isValueEqual(this.isHeaderFooterEnabled_) ||
267           !ticketStore.color.isValueEqual(this.colorValue_) ||
268           !ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
269           this.pageRanges_ == null ||
270           !areRangesEqual(ticketStore.pageRange.getPageRanges(),
271                           this.pageRanges_) ||
272           (!ticketStore.marginsType.isValueEqual(this.marginsType_) &&
273               !ticketStore.marginsType.isValueEqual(
274                   print_preview.ticket_items.MarginsType.Value.CUSTOM)) ||
275           (ticketStore.marginsType.isValueEqual(
276               print_preview.ticket_items.MarginsType.Value.CUSTOM) &&
277               !ticketStore.customMargins.isValueEqual(
278                   this.documentInfo_.margins)) ||
279           !ticketStore.cssBackground.isValueEqual(
280               this.isCssBackgroundEnabled_) ||
281           !ticketStore.selectionOnly.isValueEqual(
282               this.isSelectionOnlyEnabled_) ||
283           (this.selectedDestination_ !=
284               this.destinationStore_.selectedDestination);
285     },
287     /**
288      * Called when the page layout of the document is ready. Always occurs
289      * as a result of a preview request.
290      * @param {Event} event Contains layout info about the document.
291      * @private
292      */
293     onPageLayoutReady_: function(event) {
294       // NOTE: A request ID is not specified, so assuming its for the current
295       // in-flight request.
297       var origin = new print_preview.Coordinate2d(
298           event.pageLayout.printableAreaX,
299           event.pageLayout.printableAreaY);
300       var size = new print_preview.Size(
301           event.pageLayout.printableAreaWidth,
302           event.pageLayout.printableAreaHeight);
304       var margins = new print_preview.Margins(
305           Math.round(event.pageLayout.marginTop),
306           Math.round(event.pageLayout.marginRight),
307           Math.round(event.pageLayout.marginBottom),
308           Math.round(event.pageLayout.marginLeft));
310       var o = print_preview.ticket_items.CustomMargins.Orientation;
311       var pageSize = new print_preview.Size(
312           event.pageLayout.contentWidth +
313               margins.get(o.LEFT) + margins.get(o.RIGHT),
314           event.pageLayout.contentHeight +
315               margins.get(o.TOP) + margins.get(o.BOTTOM));
317       this.documentInfo_.updatePageInfo(
318           new print_preview.PrintableArea(origin, size),
319           pageSize,
320           event.hasCustomPageSizeStyle,
321           margins);
322     },
324     /**
325      * Called when the document page count is received from the native layer.
326      * Always occurs as a result of a preview request.
327      * @param {Event} event Contains the document's page count.
328      * @private
329      */
330     onPageCountReady_: function(event) {
331       if (this.inFlightRequestId_ != event.previewResponseId) {
332         return; // Ignore old response.
333       }
334       this.documentInfo_.updatePageCount(event.pageCount);
335       this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
336     },
338     /**
339      * Called when a page's preview has been generated. Dispatches a
340      * PAGE_READY event.
341      * @param {Event} event Contains the page index and preview UID.
342      * @private
343      */
344     onPagePreviewReady_: function(event) {
345       if (this.inFlightRequestId_ != event.previewResponseId) {
346         return; // Ignore old response.
347       }
348       var pageNumber = event.pageIndex + 1;
349       var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet();
350       if (pageNumberSet.hasPageNumber(pageNumber)) {
351         var previewIndex = pageNumberSet.getPageNumberIndex(pageNumber);
352         if (previewIndex == 0) {
353           this.dispatchPreviewStartEvent_(event.previewUid, event.pageIndex);
354         }
355         this.dispatchPageReadyEvent_(
356             previewIndex, pageNumber, event.previewUid);
357       }
358     },
360     /**
361      * Called when the preview generation is complete. Dispatches a
362      * DOCUMENT_READY event.
363      * @param {Event} event Contains the preview UID and response ID.
364      * @private
365      */
366     onPreviewGenerationDone_: function(event) {
367       if (this.inFlightRequestId_ != event.previewResponseId) {
368         return; // Ignore old response.
369       }
370       // Dispatch a PREVIEW_START event since non-modifiable documents don't
371       // trigger PAGE_READY events.
372       if (!this.documentInfo_.isModifiable) {
373         this.dispatchPreviewStartEvent_(event.previewUid, 0);
374       }
375       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
376     },
378     /**
379      * Called when the preview generation fails.
380      * @private
381      */
382     onPreviewGenerationFail_: function() {
383       // NOTE: No request ID is returned from Chromium so its assumed its the
384       // current one.
385       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL);
386     }
387   };
389   // Export
390   return {
391     PreviewGenerator: PreviewGenerator
392   };