Commit 28370024 authored by raymes@chromium.org's avatar raymes@chromium.org

Refactor pdf_scripting_api.js to remove the PDFMessagingHost in OOP PDF

This merges the PDFMessagingHost functionality into PDFViewer and renames
PDFMessagingClient to PDFScriptingAPI. PDFMessagingHost provided an extra
layer of indirection between the page communicating with the PDF viewer
and the viewer itself which made it difficult to add new functionality.

BUG=303491

Review URL: https://codereview.chromium.org/266413002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269548 0039d316-1c4b-4281-b951-d872f2087c98
parent 3b531d10
......@@ -5,8 +5,8 @@
'use strict';
<include src="../../../../ui/webui/resources/js/util.js">
<include src="viewport.js">
<include src="pdf_scripting_api.js">
<include src="viewport.js">
/**
* @return {number} Width of a scrollbar in pixels
......@@ -56,9 +56,16 @@ function PDFViewer() {
// chrome/renderer/printing/print_web_view_helper.cc actually references it.
this.plugin_.id = 'plugin';
this.plugin_.type = 'application/x-google-chrome-pdf';
this.plugin_.addEventListener('message', this.handleMessage_.bind(this),
this.plugin_.addEventListener('message', this.handlePluginMessage_.bind(this),
false);
// Handle scripting messages from outside the extension that wish to interact
// with it. We also send a message indicating that extension has loaded and
// is ready to receive messages.
window.addEventListener('message', this.handleScriptingMessage_.bind(this),
false);
this.sendScriptingMessage_({type: 'readyToReceive'});
// If the viewer is started from a MIME type request, there will be a
// background page and stream details object with the details of the request.
// Otherwise, we take the query string of the URL to indicate the URL of the
......@@ -85,8 +92,6 @@ function PDFViewer() {
this.plugin_.setAttribute('full-frame', '');
document.body.appendChild(this.plugin_);
this.messagingHost_ = new PDFMessagingHost(window, this);
this.setupEventListeners_(streamDetails);
}
......@@ -212,8 +217,9 @@ PDFViewer.prototype = {
// Document load complete.
var loadEvent = new Event('pdfload');
window.dispatchEvent(loadEvent);
// TODO(raymes): Replace this and other callbacks with events.
this.messagingHost_.documentLoaded();
this.sendScriptingMessage_({
type: 'documentLoaded'
});
if (this.lastViewportPosition_)
this.viewport_.position = this.lastViewportPosition_;
}
......@@ -237,7 +243,7 @@ PDFViewer.prototype = {
* An event handler for handling message events received from the plugin.
* @param {MessageObject} message a message event.
*/
handleMessage_: function(message) {
handlePluginMessage_: function(message) {
switch (message.data.type.toString()) {
case 'documentDimensions':
this.documentDimensions_ = message.data;
......@@ -308,7 +314,8 @@ PDFViewer.prototype = {
this.toolbar_.style.bottom = toolbarBottom + 'px';
// Update the page indicator.
this.pageIndicator_.index = this.viewport_.getMostVisiblePage();
var visiblePage = this.viewport_.getMostVisiblePage();
this.pageIndicator_.index = visiblePage;
if (this.documentDimensions_.pageDimensions.length > 1 &&
hasScrollbars.vertical) {
this.pageIndicator_.style.visibility = 'visible';
......@@ -316,8 +323,6 @@ PDFViewer.prototype = {
this.pageIndicator_.style.visibility = 'hidden';
}
this.messagingHost_.viewportChanged();
var position = this.viewport_.position;
var zoom = this.viewport_.zoom;
// Notify the plugin of the viewport change.
......@@ -327,62 +332,73 @@ PDFViewer.prototype = {
xOffset: position.x,
yOffset: position.y
});
var visiblePageDimensions = this.viewport_.getPageScreenRect(visiblePage);
var size = this.viewport_.size;
this.sendScriptingMessage_({
type: 'viewport',
pageX: visiblePageDimensions.x,
pageY: visiblePageDimensions.y,
pageWidth: visiblePageDimensions.width,
viewportWidth: size.width,
viewportHeight: size.height,
});
},
/**
* Resets the viewer into print preview mode, which is used for Chrome print
* preview.
* @param {string} url the url of the pdf to load.
* @param {boolean} grayscale true if the pdf should be displayed in
* grayscale, false otherwise.
* @param {Array.<number>} pageNumbers an array of the number to label each
* page in the document.
* @param {boolean} modifiable whether the PDF is modifiable or not.
* @private
* Handle a scripting message from outside the extension (typically sent by
* PDFScriptingAPI in a page containing the extension) to interact with the
* plugin.
* @param {MessageObject} message the message to handle.
*/
resetPrintPreviewMode: function(url,
grayscale,
pageNumbers,
modifiable) {
if (!this.inPrintPreviewMode_) {
this.inPrintPreviewMode_ = true;
this.viewport_.fitToPage();
handleScriptingMessage_: function(message) {
switch (message.data.type.toString()) {
case 'resetPrintPreviewMode':
if (!this.inPrintPreviewMode_) {
this.inPrintPreviewMode_ = true;
this.viewport_.fitToPage();
}
// Stash the scroll location so that it can be restored when the new
// document is loaded.
this.lastViewportPosition_ = this.viewport_.position;
// TODO(raymes): Disable these properly in the plugin.
var printButton = $('print-button');
if (printButton)
printButton.parentNode.removeChild(printButton);
var saveButton = $('save-button');
if (saveButton)
saveButton.parentNode.removeChild(saveButton);
this.pageIndicator_.pageLabels = message.data.pageNumbers;
this.plugin_.postMessage({
type: 'resetPrintPreviewMode',
url: message.data.url,
grayscale: message.data.grayscale,
// If the PDF isn't modifiable we send 0 as the page count so that no
// blank placeholder pages get appended to the PDF.
pageCount: (message.data.modifiable ?
message.data.pageNumbers.length : 0)
});
break;
case 'loadPreviewPage':
this.plugin_.postMessage(message.data);
break;
}
// Stash the scroll location so that it can be restored when the new
// document is loaded.
this.lastViewportPosition_ = this.viewport_.position;
// TODO(raymes): Disable these properly in the plugin.
var printButton = $('print-button');
if (printButton)
printButton.parentNode.removeChild(printButton);
var saveButton = $('save-button');
if (saveButton)
saveButton.parentNode.removeChild(saveButton);
this.pageIndicator_.pageLabels = pageNumbers;
this.plugin_.postMessage({
type: 'resetPrintPreviewMode',
url: url,
grayscale: grayscale,
// If the PDF isn't modifiable we send 0 as the page count so that no
// blank placeholder pages get appended to the PDF.
pageCount: (modifiable ? pageNumbers.length : 0)
});
},
/**
* Load a page into the document while in print preview mode.
* @param {string} url the url of the pdf page to load.
* @param {number} index the index of the page to load.
* @private
* Send a scripting message outside the extension (typically to
* PDFScriptingAPI in a page containing the extension).
* @param {Object} message the message to send.
*/
loadPreviewPage: function(url, index) {
this.plugin_.postMessage({
type: 'loadPreviewPage',
url: url,
index: index
});
sendScriptingMessage_: function(message) {
window.parent.postMessage(message, '*');
},
/**
......
......@@ -3,37 +3,40 @@
// found in the LICENSE file.
/**
* Create a new PDFMessagingClient. This provides a scripting interface to
* Create a new PDFScriptingAPI. This provides a scripting interface to
* the PDF viewer so that it can be customized by things like print preview.
* @param {HTMLIFrameElement} iframe an iframe containing the PDF viewer.
* @param {Window} window the window of the page containing the iframe.
* @param {Window} window the window of the page containing the pdf viewer.
* @param {string} extensionUrl the url of the PDF extension.
*/
function PDFMessagingClient(iframe, window, extensionUrl) {
this.iframe_ = iframe;
function PDFScriptingAPI(window, extensionUrl) {
this.extensionUrl_ = extensionUrl;
this.readyToReceive_ = false;
this.readyToReceive = false;
this.messageQueue_ = [];
window.addEventListener('message', function(event) {
if (event.origin != this.extensionUrl_)
return;
switch (event.data.type) {
case 'readyToReceive':
this.pdfWindow_ = event.source;
this.flushPendingMessages_();
break;
case 'viewportChanged':
this.viewportChangedCallback_(event.data.pageX,
event.data.pageY,
event.data.pageWidth,
event.data.viewportWidth,
event.data.viewportHeight);
case 'viewport':
if (this.viewportChangedCallback_)
this.viewportChangedCallback_(event.data.pageX,
event.data.pageY,
event.data.pageWidth,
event.data.viewportWidth,
event.data.viewportHeight);
break;
case 'documentLoaded':
this.loadCallback_();
if (this.loadCallback_)
this.loadCallback_();
break;
}
}.bind(this), false);
}
PDFMessagingClient.prototype = {
PDFScriptingAPI.prototype = {
/**
* @private
* Send a message to the extension. If we try to send messages prior to the
......@@ -42,12 +45,12 @@ PDFMessagingClient.prototype = {
* @param {MessageObject} the message to send.
*/
sendMessage_: function(message) {
if (!this.readyToReceive_) {
if (!this.readyToReceive) {
this.messageQueue_.push(message);
return;
}
this.iframe_.contentWindow.postMessage(message, this.extensionUrl_);
this.pdfWindow_.postMessage(message, this.extensionUrl_);
},
/**
......@@ -55,10 +58,10 @@ PDFMessagingClient.prototype = {
* Flushes all pending messages to the extension.
*/
flushPendingMessages_: function() {
this.readyToReceive_ = true;
this.readyToReceive = true;
while (this.messageQueue_.length != 0) {
this.iframe_.contentWindow.postMessage(this.messageQueue_.shift(),
this.extensionUrl_);
this.pdfWindow_.postMessage(this.messageQueue_.shift(),
this.extensionUrl_);
}
},
......@@ -131,7 +134,7 @@ function PDFCreateOutOfProcessPlugin(src) {
var EXTENSION_URL = 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai';
var iframe = window.document.createElement('iframe');
iframe.setAttribute('src', EXTENSION_URL + '/index.html?' + src);
var client = new PDFMessagingClient(iframe, window, EXTENSION_URL);
var client = new PDFScriptingAPI(window, EXTENSION_URL);
// Add the functions to the iframe so that they can be called directly.
iframe.setViewportChangedCallback =
......@@ -142,64 +145,3 @@ function PDFCreateOutOfProcessPlugin(src) {
iframe.sendKeyEvent = client.sendKeyEvent.bind(client);
return iframe;
}
/**
* Create a new PDFMessagingHost. This is the extension-side of the scripting
* interface to the PDF viewer. It handles requests from a page which contains
* a PDF viewer extension and translates them into actions on the viewer.
* @param {Window} window the window containing the PDF extension.
* @param {PDFViewer} pdfViewer the object which provides access to the viewer.
*/
function PDFMessagingHost(window, pdfViewer) {
this.window_ = window;
this.pdfViewer_ = pdfViewer;
this.viewport_ = pdfViewer.viewport;
window.addEventListener('message', function(event) {
switch (event.data.type) {
case 'resetPrintPreviewMode':
this.pdfViewer_.resetPrintPreviewMode(
event.data.url,
event.data.grayscale,
event.data.pageNumbers,
event.data.modifiable);
break;
case 'loadPreviewPage':
this.pdfViewer_.loadPreviewPage(event.data.url, event.data.index);
break;
}
}.bind(this), false);
if (this.window_.parent != this.window_)
this.sendMessage_({type: 'readyToReceive'});
}
PDFMessagingHost.prototype = {
sendMessage_: function(message) {
if (this.window_.parent == this.window_)
return;
this.window_.parent.postMessage(message, '*');
},
viewportChanged: function() {
var visiblePage = this.viewport_.getMostVisiblePage();
var pageDimensions = this.viewport_.getPageScreenRect(visiblePage);
var size = this.viewport_.size;
this.sendMessage_({
type: 'viewportChanged',
pageX: pageDimensions.x,
pageY: pageDimensions.y,
pageWidth: pageDimensions.width,
viewportWidth: size.width,
viewportHeight: size.height,
});
},
documentLoaded: function() {
if (this.window_.parent == this.window_)
return;
this.sendMessage_({ type: 'documentLoaded' });
}
};
......@@ -16,15 +16,10 @@ var tests = [
var sizer = document.getElementById('sizer');
chrome.test.assertEq(826, sizer.offsetWidth);
chrome.test.assertEq(1066, sizer.offsetHeight);
}
chrome.test.succeed();
},
];
function runTests() {
for (var i = 0; i < tests.length; ++i) {
console.log('Running: ' + tests[i].name);
tests[i]();
}
chrome.test.notifyPass();
}
window.addEventListener('pdfload', runTests);
window.addEventListener('pdfload', function() {
chrome.test.runTests(tests);
});
......@@ -22,6 +22,7 @@ var tests = [
chrome.test.assertTrue(
String(element.constructor).indexOf(elementNames[i]) != -1);
}
chrome.test.succeed();
},
/**
......@@ -33,15 +34,8 @@ var tests = [
chrome.test.assertTrue(
plugin.getAttribute('src').indexOf('/pdf/test.pdf') != -1);
chrome.test.succeed();
},
];
function runTests() {
for (var i = 0; i < tests.length; ++i) {
console.log('Running: ' + tests[i].name);
tests[i]();
}
chrome.test.notifyPass();
}
runTests();
chrome.test.runTests(tests);
......@@ -100,6 +100,7 @@ var tests = [
scrollbars = viewport.documentNeedsScrollbars_(0.5);
chrome.test.assertTrue(scrollbars.vertical);
chrome.test.assertFalse(scrollbars.horizontal);
chrome.test.succeed();
},
function testSetZoom() {
......@@ -149,6 +150,7 @@ var tests = [
chrome.test.assertEq('400px', mockSizer.style.height);
chrome.test.assertEq(150, mockWindow.pageXOffset);
chrome.test.assertEq(150, mockWindow.pageYOffset);
chrome.test.succeed();
},
function testGetMostVisiblePage() {
......@@ -186,6 +188,7 @@ var tests = [
viewport.setZoom_(2);
mockWindow.scrollTo(0, 151);
chrome.test.assertEq(1, viewport.getMostVisiblePage());
chrome.test.succeed();
},
function testFitToWidth() {
......@@ -253,6 +256,7 @@ var tests = [
chrome.test.assertTrue(mockCallback.wasCalled);
chrome.test.assertEq('85px', mockSizer.style.width);
chrome.test.assertEq(1.7, viewport.zoom);
chrome.test.succeed();
},
function testFitToPage() {
......@@ -337,6 +341,7 @@ var tests = [
// The page will be centred because it is less than the document width.
chrome.test.assertEq(12.5, viewport.position.x);
chrome.test.assertEq(50, viewport.position.y);
chrome.test.succeed();
},
function testGoToPage() {
......@@ -377,6 +382,7 @@ var tests = [
chrome.test.assertTrue(mockCallback.wasCalled);
chrome.test.assertEq(0, viewport.position.x);
chrome.test.assertEq(150, viewport.position.y);
chrome.test.succeed();
},
function testGetPageScreenRect() {
......@@ -419,15 +425,8 @@ var tests = [
Viewport.PAGE_SHADOW.left, rect1.width);
chrome.test.assertEq(200 - Viewport.PAGE_SHADOW.bottom -
Viewport.PAGE_SHADOW.top, rect1.height);
chrome.test.succeed();
}
];
function runTests() {
for (var i = 0; i < tests.length; ++i) {
console.log('Running: ' + tests[i].name);
tests[i]();
}
chrome.test.notifyPass();
}
runTests();
chrome.test.runTests(tests);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment