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