Home | History | Annotate | Download | only in pdf
      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  */
     11 function 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 
     46 PDFScriptingAPI.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  */
    167 function 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