1// Copyright 2014 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/**
6 * Create a new PDFScriptingAPI. This provides a scripting interface to
7 * the PDF viewer so that it can be customized by things like print preview.
8 * @param {Window} window the window of the page containing the pdf viewer.
9 * @param {string} extensionUrl the url of the PDF extension.
10 */
11function PDFScriptingAPI(window, extensionUrl) {
12  this.extensionUrl_ = extensionUrl;
13  this.messageQueue_ = [];
14  window.addEventListener('message', function(event) {
15    if (event.origin != this.extensionUrl_) {
16      console.error('Received message that was not from the extension: ' +
17                    event);
18      return;
19    }
20    switch (event.data.type) {
21      case 'readyToReceive':
22        this.setDestinationWindow(event.source);
23        break;
24      case 'viewport':
25        if (this.viewportChangedCallback_)
26          this.viewportChangedCallback_(event.data.pageX,
27                                        event.data.pageY,
28                                        event.data.pageWidth,
29                                        event.data.viewportWidth,
30                                        event.data.viewportHeight);
31        break;
32      case 'documentLoaded':
33        if (this.loadCallback_)
34          this.loadCallback_();
35        break;
36      case 'getAccessibilityJSONReply':
37        if (this.accessibilityCallback_) {
38          this.accessibilityCallback_(event.data.json);
39          this.accessibilityCallback_ = null;
40        }
41        break;
42    }
43  }.bind(this), false);
44}
45
46PDFScriptingAPI.prototype = {
47  /**
48   * @private
49   * Send a message to the extension. If we try to send messages prior to the
50   * extension being ready to receive messages (i.e. before it has finished
51   * loading) we queue up the messages and flush them later.
52   * @param {Object} the message to send.
53   */
54  sendMessage_: function(message) {
55    if (!this.pdfWindow_) {
56      this.messageQueue_.push(message);
57      return;
58    }
59
60    this.pdfWindow_.postMessage(message, this.extensionUrl_);
61  },
62
63  /**
64   * Sets the destination window containing the PDF viewer. This will be called
65   * when a 'readyToReceive' message is received from the PDF viewer or it can
66   * be called during tests. It then flushes any pending messages to the window.
67   * @param {Window} pdfWindow the window containing the PDF viewer.
68   */
69  setDestinationWindow: function(pdfWindow) {
70    this.pdfWindow_ = pdfWindow;
71    while (this.messageQueue_.length != 0) {
72      this.pdfWindow_.postMessage(this.messageQueue_.shift(),
73                                  this.extensionUrl_);
74    }
75  },
76
77  /**
78   * Sets the callback which will be run when the PDF viewport changes.
79   * @param {Function} callback the callback to be called.
80   */
81  setViewportChangedCallback: function(callback) {
82    this.viewportChangedCallback_ = callback;
83  },
84
85  /**
86   * Sets the callback which will be run when the PDF document has finished
87   * loading.
88   * @param {Function} callback the callback to be called.
89   */
90  setLoadCallback: function(callback) {
91    this.loadCallback_ = callback;
92  },
93
94  /**
95   * Resets the PDF viewer into print preview mode.
96   * @param {string} url the url of the PDF to load.
97   * @param {boolean} grayscale whether or not to display the PDF in grayscale.
98   * @param {Array.<number>} pageNumbers an array of the page numbers.
99   * @param {boolean} modifiable whether or not the document is modifiable.
100   */
101  resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) {
102    this.sendMessage_({
103      type: 'resetPrintPreviewMode',
104      url: url,
105      grayscale: grayscale,
106      pageNumbers: pageNumbers,
107      modifiable: modifiable
108    });
109  },
110
111  /**
112   * Load a page into the document while in print preview mode.
113   * @param {string} url the url of the pdf page to load.
114   * @param {number} index the index of the page to load.
115   */
116  loadPreviewPage: function(url, index) {
117    this.sendMessage_({
118      type: 'loadPreviewPage',
119      url: url,
120      index: index
121    });
122  },
123
124  /**
125   * Get accessibility JSON for the document.
126   * @param {Function} callback a callback to be called with the accessibility
127   *     json that has been retrieved.
128   * @param {number} [page] the 0-indexed page number to get accessibility data
129   *     for. If this is not provided, data about the entire document is
130   *     returned.
131   * @return {boolean} true if the function is successful, false if there is an
132   *     outstanding request for accessibility data that has not been answered.
133   */
134  getAccessibilityJSON: function(callback, page) {
135    if (this.accessibilityCallback_)
136      return false;
137    this.accessibilityCallback_ = callback;
138    var message = {
139      type: 'getAccessibilityJSON',
140    };
141    if (page || page == 0)
142      message.page = page;
143    this.sendMessage_(message);
144    return true;
145  },
146
147  /**
148   * Send a key event to the extension.
149   * @param {number} keyCode the key code to send to the extension.
150   */
151  sendKeyEvent: function(keyCode) {
152    this.sendMessage_({
153      type: 'sendKeyEvent',
154      keyCode: keyCode
155    });
156  },
157};
158
159/**
160 * Creates a PDF viewer with a scripting interface. This is basically 1) an
161 * iframe which is navigated to the PDF viewer extension and 2) a scripting
162 * interface which provides access to various features of the viewer for use
163 * by print preview and accessbility.
164 * @param {string} src the source URL of the PDF to load initially.
165 * @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
166 */
167function PDFCreateOutOfProcessPlugin(src) {
168  var EXTENSION_URL = 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai';
169  var iframe = window.document.createElement('iframe');
170  iframe.setAttribute('src', EXTENSION_URL + '/index.html?' + src);
171  var client = new PDFScriptingAPI(window, EXTENSION_URL);
172
173  // Add the functions to the iframe so that they can be called directly.
174  iframe.setViewportChangedCallback =
175      client.setViewportChangedCallback.bind(client);
176  iframe.setLoadCallback = client.setLoadCallback.bind(client);
177  iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client);
178  iframe.loadPreviewPage = client.loadPreviewPage.bind(client);
179  iframe.sendKeyEvent = client.sendKeyEvent.bind(client);
180  return iframe;
181}
182