- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / print_preview / preview_generator.js
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 cr.define('print_preview', function() {
6   'use strict';
7
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);
23
24     /**
25      * Used to get the currently selected destination.
26      * @type {!print_preview.DestinationStore}
27      * @private
28      */
29     this.destinationStore_ = destinationStore;
30
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;
37
38     /**
39      * Interface to the Chromium native layer.
40      * @type {!print_preview.NativeLayer}
41      * @private
42      */
43     this.nativeLayer_ = nativeLayer;
44
45     /**
46      * Document data model.
47      * @type {!print_preview.DocumentInfo}
48      * @private
49      */
50     this.documentInfo_ = documentInfo;
51
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;
59
60     /**
61      * Whether the previews are being generated in landscape mode.
62      * @type {boolean}
63      * @private
64      */
65     this.isLandscapeEnabled_ = false;
66
67     /**
68      * Whether the previews are being generated with a header and footer.
69      * @type {boolean}
70      * @private
71      */
72     this.isHeaderFooterEnabled_ = false;
73
74     /**
75      * Whether the previews are being generated in color.
76      * @type {boolean}
77      * @private
78      */
79     this.colorValue_ = false;
80
81     /**
82      * Whether the document should be fitted to the page.
83      * @type {boolean}
84      * @private
85      */
86     this.isFitToPageEnabled_ = false;
87
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;
94
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;
101
102     /**
103      * Whether the document should have element CSS backgrounds printed.
104      * @type {boolean}
105      * @private
106      */
107     this.isCssBackgroundEnabled_ = false;
108
109     /**
110      * Destination that was selected for the last preview.
111      * @type {print_preview.Destination}
112      * @private
113      */
114     this.selectedDestination_ = null;
115
116     /**
117      * Event tracker used to keep track of native layer events.
118      * @type {!EventTracker}
119      * @private
120      */
121     this.tracker_ = new EventTracker();
122
123     this.addEventListeners_();
124   };
125
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',
133
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',
139
140     // Dispatched when the document preview starts to be generated.
141     PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START',
142
143     // Dispatched when the current print preview request fails.
144     FAIL: 'print_preview.PreviewGenerator.FAIL'
145   };
146
147   PreviewGenerator.prototype = {
148     __proto__: cr.EventTarget.prototype,
149
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;
178
179       this.inFlightRequestId_++;
180       this.nativeLayer_.startGetPreview(
181           this.destinationStore_.selectedDestination,
182           this.printTicketStore_,
183           this.documentInfo_,
184           this.inFlightRequestId_);
185       return true;
186     },
187
188     /** Removes all event listeners that the preview generator has attached. */
189     removeEventListeners: function() {
190       this.tracker_.removeAll();
191     },
192
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.PREVIEW_RELOAD,
209           this.onPreviewReload_.bind(this));
210       this.tracker_.add(
211           this.nativeLayer_,
212           print_preview.NativeLayer.EventType.PAGE_PREVIEW_READY,
213           this.onPagePreviewReady_.bind(this));
214       this.tracker_.add(
215           this.nativeLayer_,
216           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_DONE,
217           this.onPreviewGenerationDone_.bind(this));
218       this.tracker_.add(
219           this.nativeLayer_,
220           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_FAIL,
221           this.onPreviewGenerationFail_.bind(this));
222     },
223
224     /**
225      * Dispatches a PAGE_READY event to signal that a page preview is ready.
226      * @param {number} previewIndex Index of the page with respect to the pages
227      *     shown in the preview. E.g an index of 0 is the first displayed page,
228      *     but not necessarily the first original document page.
229      * @param {number} pageNumber Number of the page with respect to the
230      *     document. A value of 3 means it's the third page of the original
231      *     document.
232      * @param {number} previewUid Unique identifier of the preview.
233      * @private
234      */
235     dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) {
236       var pageGenEvent = new Event(PreviewGenerator.EventType.PAGE_READY);
237       pageGenEvent.previewIndex = previewIndex;
238       pageGenEvent.previewUrl = 'chrome://print/' + previewUid.toString() +
239           '/' + (pageNumber - 1) + '/print.pdf';
240       this.dispatchEvent(pageGenEvent);
241     },
242
243     /**
244      * Dispatches a PREVIEW_START event. Signals that the preview should be
245      * reloaded.
246      * @param {number} previewUid Unique identifier of the preview.
247      * @param {number} index Index of the first page of the preview.
248      * @private
249      */
250     dispatchPreviewStartEvent_: function(previewUid, index) {
251       var previewStartEvent = new Event(
252           PreviewGenerator.EventType.PREVIEW_START);
253       if (!this.documentInfo_.isModifiable) {
254         index = -1;
255       }
256       previewStartEvent.previewUrl = 'chrome://print/' +
257           previewUid.toString() + '/' + index + '/print.pdf';
258       this.dispatchEvent(previewStartEvent);
259     },
260
261     /**
262      * @return {boolean} Whether the print ticket has changed sufficiently to
263      *     determine whether a new preview request should be issued.
264      * @private
265      */
266     hasPreviewChanged_: function() {
267       var ticketStore = this.printTicketStore_;
268       return this.inFlightRequestId_ == -1 ||
269           !ticketStore.landscape.isValueEqual(this.isLandscapeEnabled_) ||
270           !ticketStore.headerFooter.isValueEqual(this.isHeaderFooterEnabled_) ||
271           !ticketStore.color.isValueEqual(this.colorValue_) ||
272           !ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
273           this.pageRanges_ == null ||
274           !areRangesEqual(ticketStore.pageRange.getPageRanges(),
275                           this.pageRanges_) ||
276           (!ticketStore.marginsType.isValueEqual(this.marginsType_) &&
277               !ticketStore.marginsType.isValueEqual(
278                   print_preview.ticket_items.MarginsType.Value.CUSTOM)) ||
279           (ticketStore.marginsType.isValueEqual(
280               print_preview.ticket_items.MarginsType.Value.CUSTOM) &&
281               !ticketStore.customMargins.isValueEqual(
282                   this.documentInfo_.margins)) ||
283           !ticketStore.cssBackground.isValueEqual(
284               this.isCssBackgroundEnabled_) ||
285           !ticketStore.selectionOnly.isValueEqual(
286               this.isSelectionOnlyEnabled_) ||
287           (this.selectedDestination_ !=
288               this.destinationStore_.selectedDestination);
289     },
290
291     /**
292      * Called when the page layout of the document is ready. Always occurs
293      * as a result of a preview request.
294      * @param {Event} event Contains layout info about the document.
295      * @private
296      */
297     onPageLayoutReady_: function(event) {
298       // NOTE: A request ID is not specified, so assuming its for the current
299       // in-flight request.
300
301       var origin = new print_preview.Coordinate2d(
302           event.pageLayout.printableAreaX,
303           event.pageLayout.printableAreaY);
304       var size = new print_preview.Size(
305           event.pageLayout.printableAreaWidth,
306           event.pageLayout.printableAreaHeight);
307
308       var margins = new print_preview.Margins(
309           Math.round(event.pageLayout.marginTop),
310           Math.round(event.pageLayout.marginRight),
311           Math.round(event.pageLayout.marginBottom),
312           Math.round(event.pageLayout.marginLeft));
313
314       var o = print_preview.ticket_items.CustomMargins.Orientation;
315       var pageSize = new print_preview.Size(
316           event.pageLayout.contentWidth +
317               margins.get(o.LEFT) + margins.get(o.RIGHT),
318           event.pageLayout.contentHeight +
319               margins.get(o.TOP) + margins.get(o.BOTTOM));
320
321       this.documentInfo_.updatePageInfo(
322           new print_preview.PrintableArea(origin, size),
323           pageSize,
324           event.hasCustomPageSizeStyle,
325           margins);
326     },
327
328     /**
329      * Called when the document page count is received from the native layer.
330      * Always occurs as a result of a preview request.
331      * @param {Event} event Contains the document's page count.
332      * @private
333      */
334     onPageCountReady_: function(event) {
335       if (this.inFlightRequestId_ != event.previewResponseId) {
336         return; // Ignore old response.
337       }
338       this.documentInfo_.updatePageCount(event.pageCount);
339       this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
340     },
341
342     /**
343      * Called when the print preview should be reloaded.
344      * @param {Event} event Contains the preview UID and request ID.
345      * @private
346      */
347     onPreviewReload_: function(event) {
348       if (this.inFlightRequestId_ != event.previewResponseId) {
349         return; // Ignore old response.
350       }
351       var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet();
352       this.dispatchPreviewStartEvent_(
353           event.previewUid, pageNumberSet.getPageNumberAt(0) - 1);
354       for (var i = 0; i < pageNumberSet.size; i++) {
355         var pageNumber = pageNumberSet.getPageNumberAt(i);
356         this.dispatchPageReadyEvent_(i, pageNumber, event.previewUid);
357       }
358       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
359     },
360
361     /**
362      * Called when a page's preview has been generated. Dispatches a
363      * PAGE_READY event.
364      * @param {Event} event Contains the page index and preview UID.
365      * @private
366      */
367     onPagePreviewReady_: function(event) {
368       if (this.inFlightRequestId_ != event.previewResponseId) {
369         return; // Ignore old response.
370       }
371       var pageNumber = event.pageIndex + 1;
372       var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet();
373       if (pageNumberSet.hasPageNumber(pageNumber)) {
374         var previewIndex = pageNumberSet.getPageNumberIndex(pageNumber);
375         if (previewIndex == 0) {
376           this.dispatchPreviewStartEvent_(event.previewUid, event.pageIndex);
377         }
378         this.dispatchPageReadyEvent_(
379             previewIndex, pageNumber, event.previewUid);
380       }
381     },
382
383     /**
384      * Called when the preview generation is complete. Dispatches a
385      * DOCUMENT_READY event.
386      * @param {Event} event Contains the preview UID and response ID.
387      * @private
388      */
389     onPreviewGenerationDone_: function(event) {
390       if (this.inFlightRequestId_ != event.previewResponseId) {
391         return; // Ignore old response.
392       }
393       // Dispatch a PREVIEW_START event since non-modifiable documents don't
394       // trigger PAGE_READY events.
395       if (!this.documentInfo_.isModifiable) {
396         this.dispatchPreviewStartEvent_(event.previewUid, 0);
397       }
398       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
399     },
400
401     /**
402      * Called when the preview generation fails.
403      * @private
404      */
405     onPreviewGenerationFail_: function() {
406       // NOTE: No request ID is returned from Chromium so its assumed its the
407       // current one.
408       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL);
409     }
410   };
411
412   // Export
413   return {
414     PreviewGenerator: PreviewGenerator
415   };
416 });