summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/pdf
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/chrome/browser/resources/pdf
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/chrome/browser/resources/pdf')
-rw-r--r--chromium/chrome/browser/resources/pdf/background.js37
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/OWNERS3
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/codereview.settings7
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_page.pngbin0 -> 1776 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_width.pngbin0 -> 1268 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_play.pngbin0 -> 1087 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_print.pngbin0 -> 904 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_save.pngbin0 -> 979 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_in.pngbin0 -> 1859 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_out.pngbin0 -> 1803 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_page.pngbin0 -> 999 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_width.pngbin0 -> 696 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_play.pngbin0 -> 664 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_print.pngbin0 -> 797 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_save.pngbin0 -> 695 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_in.pngbin0 -> 1115 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_out.pngbin0 -> 1079 bytes
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.css37
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.html7
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.js33
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.css18
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.html7
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.js5
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.css35
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.html8
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.js42
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.css41
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.html14
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.js51
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.css71
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.html12
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.js43
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.css21
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.html9
-rw-r--r--chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.js57
-rw-r--r--chromium/chrome/browser/resources/pdf/includes.js12
-rw-r--r--chromium/chrome/browser/resources/pdf/index.css44
-rw-r--r--chromium/chrome/browser/resources/pdf/index.html57
-rw-r--r--chromium/chrome/browser/resources/pdf/manifest.json6
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf.html15
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf.js512
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf_extension_test.cc103
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf_scripting_api.js181
-rw-r--r--chromium/chrome/browser/resources/pdf/polymer_loader.js12
-rw-r--r--chromium/chrome/browser/resources/pdf/viewport.js470
45 files changed, 1907 insertions, 63 deletions
diff --git a/chromium/chrome/browser/resources/pdf/background.js b/chromium/chrome/browser/resources/pdf/background.js
index 0a37f6d6313..2114010193f 100644
--- a/chromium/chrome/browser/resources/pdf/background.js
+++ b/chromium/chrome/browser/resources/pdf/background.js
@@ -2,11 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-chrome.streamsPrivate.onExecuteMimeTypeHandler.addListener(
- function(mime_type, original_url, content_url, tab_id) {
- // TODO(raymes): Currently this doesn't work with embedded PDFs (it
- // causes the entire frame to navigate). Also work out how we can
- // mask the URL with the URL of the PDF.
- chrome.tabs.update(tab_id, { url: 'pdf.html?' + content_url });
- }
-);
+(function() {
+ 'use strict';
+
+ /**
+ * Keep a stack of stream details for requests. These are pushed onto the
+ * stack as requests come in and popped off the stack as they are handled by a
+ * renderer.
+ * TODO(raymes): This is probably racy for multiple requests. We could
+ * associate an ID with the request but this code will probably change
+ * completely when MIME type handling is improved.
+ */
+ var streamsCache = [];
+
+ window.popStreamDetails = function() {
+ if (streamsCache.length > 0)
+ return streamsCache.pop();
+ };
+
+ chrome.streamsPrivate.onExecuteMimeTypeHandler.addListener(
+ function(streamDetails) {
+ // TODO(raymes): Currently this doesn't work with embedded PDFs (it
+ // causes the entire frame to navigate). Also work out how we can
+ // mask the URL with the URL of the PDF.
+ streamsCache.push(streamDetails);
+ chrome.tabs.update(streamDetails.tabId, {url: 'index.html'});
+ }
+ );
+
+}());
diff --git a/chromium/chrome/browser/resources/pdf/html_office/OWNERS b/chromium/chrome/browser/resources/pdf/html_office/OWNERS
new file mode 100644
index 00000000000..feedefa65bd
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/OWNERS
@@ -0,0 +1,3 @@
+ganetsky@chromium.org
+jliebrand@chromium.org
+raymes@chromium.org
diff --git a/chromium/chrome/browser/resources/pdf/html_office/codereview.settings b/chromium/chrome/browser/resources/pdf/html_office/codereview.settings
new file mode 100644
index 00000000000..ca62ae98a60
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/codereview.settings
@@ -0,0 +1,7 @@
+# This file is used by gcl to get repository specific information.
+CODE_REVIEW_SERVER: codereview.chromium.org
+CC_LIST: raymes@chromium.org,ganetsky@chromium.org,jliebrand@chromium.org
+STATUS: http://chromium-status.appspot.com/status
+TRY_ON_UPLOAD: False
+GITCL_PREUPLOAD: http://src.chromium.org/viewvc/trunk/tools/depot_tools/git-cl-upload-hook?revision=HEAD&root=chrome
+GITCL_PREDCOMMIT: http://src.chromium.org/viewvc/trunk/tools/depot_tools/git-cl-upload-hook?revision=HEAD&root=chrome
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_page.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_page.png
new file mode 100644
index 00000000000..6977d2b8796
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_page.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_width.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_width.png
new file mode 100644
index 00000000000..d9dea06a74f
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_fit_width.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_play.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_play.png
new file mode 100644
index 00000000000..ac73ffc1724
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_play.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_print.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_print.png
new file mode 100644
index 00000000000..7fa75a3df72
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_print.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_save.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_save.png
new file mode 100644
index 00000000000..ea125115ed7
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_save.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_in.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_in.png
new file mode 100644
index 00000000000..2cedf813153
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_in.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_out.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_out.png
new file mode 100644
index 00000000000..e27c272fd80
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/hiDPI/button_zoom_out.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_page.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_page.png
new file mode 100644
index 00000000000..e8e7a7039cf
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_page.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_width.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_width.png
new file mode 100644
index 00000000000..700f4861ea5
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_fit_width.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_play.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_play.png
new file mode 100644
index 00000000000..d5ceae76ac8
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_play.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_print.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_print.png
new file mode 100644
index 00000000000..e044a28837f
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_print.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_save.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_save.png
new file mode 100644
index 00000000000..e732a442f35
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_save.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_in.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_in.png
new file mode 100644
index 00000000000..e05ae81589a
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_in.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_out.png b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_out.png
new file mode 100644
index 00000000000..b76b54c95fc
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/img/lowDPI/button_zoom_out.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.css b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.css
new file mode 100644
index 00000000000..d4aff70c49d
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.css
@@ -0,0 +1,37 @@
+/* Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#icon {
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ height: 100%;
+ width: 100%;
+}
+
+:host {
+ -webkit-user-select: none;
+ background-image: linear-gradient(rgb(60, 80, 119), rgb(15, 24, 41));
+ border: 1px solid rgb(11, 9, 16);
+ cursor: default;
+ display: inline-block;
+ height: 36px;
+ margin: 0;
+ pointer-events: all;
+ width: 43px;
+}
+
+:host(:focus:host) {
+ outline: none;
+}
+
+:host(:hover:host) {
+ background-image: linear-gradient(rgb(73, 102, 155), rgb(32, 52, 95));
+}
+
+:host(.latchable.polymer-selected:host),
+:host(:active:host) {
+ background-color: rgb(75, 103, 156);
+ background-image: none;
+}
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.html b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.html
new file mode 100644
index 00000000000..e1eee711067
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.html
@@ -0,0 +1,7 @@
+<polymer-element name="viewer-button" attributes="img latchable">
+<template>
+ <link rel="stylesheet" href="viewer-button.css">
+ <div id="icon"></div>
+</template>
+<script src="viewer-button.js"></script>
+</polymer-element>
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.js b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.js
new file mode 100644
index 00000000000..73bd53eff1e
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-button/viewer-button.js
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+ var dpi = '';
+
+ Polymer('viewer-button', {
+ img: '',
+ latchable: false,
+ ready: function() {
+ if (!dpi) {
+ var mql = window.matchMedia('(-webkit-min-device-pixel-ratio: 1.3');
+ dpi = mql.matches ? 'hi' : 'low';
+ }
+ },
+ imgChanged: function() {
+ if (this.img) {
+ this.$.icon.style.backgroundImage =
+ 'url(' + this.getAttribute('assetpath') + 'img/' + dpi +
+ 'DPI/' + this.img + ')';
+ } else {
+ this.$.icon.style.backgroundImage = '';
+ }
+ },
+ latchableChanged: function() {
+ if (this.latchable)
+ this.classList.add('latchable');
+ else
+ this.classList.remove('latchable');
+ },
+ });
+})();
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.css b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.css
new file mode 100644
index 00000000000..9c7c7c82676
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.css
@@ -0,0 +1,18 @@
+:host {
+ background-color: #ccc;
+ color: #555;
+ font-family: sans-serif;
+ font-size: 20px;
+ height: 100%;
+ pointer-events: none;
+ position: fixed;
+ text-align: center;
+ width: 100%;
+}
+
+#load-failed-message {
+ line-height: 0;
+ position: absolute;
+ top: 50%;
+ width: 100%;
+} \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.html b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.html
new file mode 100644
index 00000000000..81199fe7d3a
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.html
@@ -0,0 +1,7 @@
+<polymer-element name="viewer-error-screen" attributes="text">
+<template>
+ <link rel="stylesheet" href="viewer-error-screen.css">
+ <div id="load-failed-message">{{text}}</div>
+</template>
+<script src="viewer-error-screen.js"></script>
+</polymer-element>
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.js b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.js
new file mode 100644
index 00000000000..c63874dd2d7
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-error-screen/viewer-error-screen.js
@@ -0,0 +1,5 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer('viewer-error-screen', {});
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.css b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.css
new file mode 100644
index 00000000000..ec90ec99f53
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.css
@@ -0,0 +1,35 @@
+/* Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+:host {
+ -webkit-transition: opacity 400ms ease-in-out;
+ pointer-events: none;
+ position: fixed;
+ right: 0;
+}
+
+#text {
+ background-color: rgba(0, 0, 0, 0.5);
+ border-radius: 5px;
+ color: white;
+ float: left;
+ font-family: sans-serif;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 48px;
+ text-align: center;
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
+ width: 62px;
+}
+
+#triangle-right {
+ border-bottom: 6px solid transparent;
+ border-left: 8px solid rgba(0, 0, 0, 0.5);
+ border-top: 6px solid transparent;
+ display: inline;
+ float: left;
+ height: 0;
+ margin-top: 18px;
+ width: 0;
+} \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.html b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.html
new file mode 100644
index 00000000000..de59e1a9eca
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.html
@@ -0,0 +1,8 @@
+<polymer-element name="viewer-page-indicator" attributes="index label">
+<template>
+ <link rel="stylesheet" href="viewer-page-indicator.css">
+ <div id="text">{{label}}</div>
+ <div id="triangle-right"></div>
+</template>
+<script src="viewer-page-indicator.js"></script>
+</polymer-element>
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.js b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.js
new file mode 100644
index 00000000000..8612e170348
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-page-indicator/viewer-page-indicator.js
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer('viewer-page-indicator', {
+ label: '1',
+ index: 0,
+ timerId: undefined,
+ pageLabels: null,
+ ready: function() {
+ var callback = this.fadeIn.bind(this, 2000);
+ window.addEventListener('scroll', function() {
+ requestAnimationFrame(callback);
+ });
+ },
+ initialFadeIn: function() {
+ this.fadeIn(6000);
+ },
+ fadeIn: function(displayTime) {
+ var percent = window.scrollY /
+ (document.body.scrollHeight -
+ document.documentElement.clientHeight);
+ this.style.top = percent *
+ (document.documentElement.clientHeight - this.offsetHeight) + 'px';
+ this.style.opacity = 1;
+ clearTimeout(this.timerId);
+
+ this.timerId = setTimeout(function() {
+ this.style.opacity = 0;
+ this.timerId = undefined;
+ }.bind(this), displayTime);
+ },
+ pageLabelsChanged: function() {
+ this.indexChanged();
+ },
+ indexChanged: function() {
+ if (this.pageLabels)
+ this.label = this.pageLabels[this.index];
+ else
+ this.label = String(this.index + 1);
+ }
+});
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.css b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.css
new file mode 100644
index 00000000000..183f439f989
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.css
@@ -0,0 +1,41 @@
+:host {
+ -webkit-transition: opacity 400ms ease-in-out;
+ background-color: #ccc;
+ color: #555;
+ display: table;
+ font-family: sans-serif;
+ font-size: 15px;
+ height: 100%;
+ pointer-events: none;
+ position: fixed;
+ text-align: center;
+ width: 100%;
+}
+
+#message {
+ padding-bottom: 10px;
+}
+
+.center {
+ display: table-cell;
+ vertical-align: middle;
+}
+
+.form {
+ border: 1px solid #777;
+ box-shadow: 1px 1px 1px;
+ display: inline-block;
+ padding: 10px;
+ width: 300px;
+}
+
+#successMessage {
+ display: inline-block;
+ padding-left: 5px;
+ pointer-events: none;
+}
+
+input {
+ color: #333;
+ pointer-events: all;
+} \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.html b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.html
new file mode 100644
index 00000000000..24a82330987
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.html
@@ -0,0 +1,14 @@
+<polymer-element name="viewer-password-screen" attributes="text active">
+<template>
+ <link rel="stylesheet" href="viewer-password-screen.css">
+ <div class="center">
+ <form class="form">
+ <div id="message">{{text}}</div>
+ <input id="password" type="password" size="20"></input>
+ <input id="submit" type="submit" on-click={{submit}}></input>
+ <div id="successMessage">{{successMessage}}</div>
+ </form>
+ </div>
+</template>
+<script src="viewer-password-screen.js"></script>
+</polymer-element>
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.js b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.js
new file mode 100644
index 00000000000..9ba949f09b6
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-password-screen/viewer-password-screen.js
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer('viewer-password-screen', {
+ text: 'This document is password protected. Please enter a password.',
+ active: false,
+ timerId: undefined,
+ ready: function () {
+ this.activeChanged();
+ },
+ accept: function() {
+ this.successMessage = '&#x2714;' // Tick.
+ this.$.successMessage.style.color = 'rgb(0,125,0)';
+ this.active = false;
+ },
+ deny: function() {
+ this.successMessage = '&#x2718;'; // Cross.
+ this.$.successMessage.style.color = 'rgb(255,0,0)';
+ this.$.password.disabled = false;
+ this.$.submit.disabled = false;
+ this.$.password.focus();
+ this.$.password.select();
+ },
+ submit: function(e) {
+ // Prevent the default form submission behavior.
+ e.preventDefault();
+ if (this.$.password.value.length == 0)
+ return;
+ this.successMessage = '...';
+ this.$.successMessage.style.color = 'rgb(0,0,0)';
+ this.$.password.disabled = true;
+ this.$.submit.disabled = true;
+ this.fire('password-submitted', {password: this.$.password.value});
+ },
+ activeChanged: function() {
+ clearTimeout(this.timerId);
+ this.timerId = undefined;
+ if (this.active) {
+ this.style.visibility = 'visible';
+ this.style.opacity = 1;
+ this.successMessage = '';
+ this.$.password.focus();
+ } else {
+ this.style.opacity = 0;
+ this.timerId = setTimeout(function() {
+ this.style.visibility = 'hidden'
+ }.bind(this), 400);
+ }
+ }
+}); \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.css b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.css
new file mode 100644
index 00000000000..f5c13e30114
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.css
@@ -0,0 +1,71 @@
+/* Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+:host {
+ -webkit-transition: opacity 400ms ease-in-out;
+ background: rgb(29, 39, 57);
+ border-radius: 5px;
+ bottom: 26px;
+ box-shadow: 0 1px 2px gray, 0 3px 3px rgba(0, 0, 0, .2);
+ height: auto;
+ left: 26px;
+ pointer-events: none;
+ position: fixed;
+ width: auto;
+}
+
+.scaler {
+ -webkit-transform: scale(0.25);
+ -webkit-transform-origin: 0 0;
+ float: left;
+ height: 44px;
+ margin: 8px;
+ width: 44px;
+}
+
+#segments {
+ border-radius: 50%;
+ height: 176px;
+ list-style: none;
+ margin: 0;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 176px;
+}
+
+.segment {
+ -webkit-transform-origin: 0 100%;
+ background: rgb(227, 234, 249);
+ box-shadow: 0 0 0 6px rgb(29, 39, 57) inset;
+ height: 50%;
+ overflow: hidden;
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 50%;
+}
+
+.center-circle {
+ background-color: rgb(29, 39, 57);
+ border-radius: 50%;
+ height: 80px;
+ left: 44px;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ top: 44px;
+ width: 80px;
+}
+
+#text {
+ color: rgb(227, 234, 249);
+ float: left;
+ font-family: sans-serif;
+ font-size: 16px;
+ font-weight: bold;
+ line-height: 58px;
+ margin-right: 10px;
+ margin-top: 1px;
+}
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.html b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.html
new file mode 100644
index 00000000000..bc16ef7a383
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.html
@@ -0,0 +1,12 @@
+<polymer-element name="viewer-progress-bar"
+ attributes="progress text numSegments">
+<template>
+ <link rel="stylesheet" href="viewer-progress-bar.css">
+ <div class="scaler">
+ <ul id="segments"></ul>
+ <div class="center-circle"></div>
+ </div>
+ <div id="text">{{text}}</div>
+</template>
+<script src="viewer-progress-bar.js"></script>
+</polymer-element>
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.js b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.js
new file mode 100644
index 00000000000..7c4932dec92
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-progress-bar/viewer-progress-bar.js
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer('viewer-progress-bar', {
+ progress: 0,
+ text: 'Loading',
+ numSegments: 8,
+ segments: [],
+ ready: function() {
+ this.numSegmentsChanged();
+ },
+ progressChanged: function() {
+ var numVisible = this.progress * this.segments.length / 100.0;
+ for (var i = 0; i < this.segments.length; i++) {
+ this.segments[i].style.visibility =
+ i < numVisible ? 'visible' : 'hidden';
+ }
+
+ if (this.progress >= 100 || this.progress < 0)
+ this.style.opacity = 0;
+ },
+ numSegmentsChanged: function() {
+ // Clear the existing segments.
+ this.segments = [];
+ var segmentsElement = this.$.segments;
+ segmentsElement.innerHTML = '';
+
+ // Create the new segments.
+ var segment = document.createElement('li');
+ segment.classList.add('segment');
+ var angle = 360 / this.numSegments;
+ for (var i = 0; i < this.numSegments; ++i) {
+ var segmentCopy = segment.cloneNode(true);
+ segmentCopy.style.webkitTransform =
+ 'rotate(' + (i * angle) + 'deg) skewY(' +
+ -1 * (90 - angle) + 'deg)';
+ segmentsElement.appendChild(segmentCopy);
+ this.segments.push(segmentCopy);
+ }
+ this.progressChanged();
+ }
+});
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.css b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.css
new file mode 100644
index 00000000000..64be0e9f12c
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.css
@@ -0,0 +1,21 @@
+/* Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+:host {
+ -webkit-transition: opacity 400ms ease-in-out;
+ bottom: 0;
+ display: block;
+ font-size: 0;
+ opacity: 1;
+ padding: 30px 30px 15px 30vw;
+ pointer-events: none;
+ position: fixed;
+ right: 0;
+}
+
+#toolbar {
+ border-radius: 3px;
+ box-shadow: 0 1px 2px gray, 0 3px 3px rgba(0, 0, 0, .2);
+ overflow: hidden;
+}
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.html b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.html
new file mode 100644
index 00000000000..a22bf997418
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.html
@@ -0,0 +1,9 @@
+<polymer-element name="viewer-toolbar" attributes="fadingIn">
+<template>
+ <link rel="stylesheet" href="viewer-toolbar.css">
+ <div id="toolbar">
+ <content></content>
+ </div>
+</template>
+<script src="viewer-toolbar.js"></script>
+</polymer-element>
diff --git a/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.js b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.js
new file mode 100644
index 00000000000..c28b45b670b
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/html_office/elements/viewer-toolbar/viewer-toolbar.js
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer('viewer-toolbar', {
+ fadingIn: false,
+ timerId_: undefined,
+ inInitialFadeIn_: false,
+ ready: function() {
+ this.mousemoveCallback = function(e) {
+ var rect = this.getBoundingClientRect();
+ if (e.clientX >= rect.left && e.clientX <= rect.right &&
+ e.clientY >= rect.top && e.clientY <= rect.bottom) {
+ this.fadingIn = true;
+ // If we hover over the toolbar, cancel the initial fade in.
+ if (this.inInitialFadeIn_)
+ this.inInitialFadeIn_ = false;
+ } else {
+ // Initially we want to keep the toolbar up for a longer period.
+ if (!this.inInitialFadeIn_)
+ this.fadingIn = false;
+ }
+ }.bind(this);
+ },
+ attached: function() {
+ this.parentNode.addEventListener('mousemove', this.mousemoveCallback);
+ },
+ detached: function() {
+ this.parentNode.removeEventListener('mousemove', this.mousemoveCallback);
+ },
+ initialFadeIn: function() {
+ this.inInitialFadeIn_ = true;
+ this.fadeIn();
+ this.fadeOutAfterDelay(6000);
+ },
+ fadingInChanged: function() {
+ if (this.fadingIn) {
+ this.fadeIn();
+ } else {
+ if (this.timerId_ === undefined)
+ this.fadeOutAfterDelay(3000);
+ }
+ },
+ fadeIn: function() {
+ this.style.opacity = 1;
+ clearTimeout(this.timerId_);
+ this.timerId_ = undefined;
+ },
+ fadeOutAfterDelay: function(delay) {
+ this.timerId_ = setTimeout(
+ function() {
+ this.style.opacity = 0;
+ this.timerId_ = undefined;
+ this.inInitialFadeIn_ = false;
+ }.bind(this), delay);
+ }
+});
diff --git a/chromium/chrome/browser/resources/pdf/includes.js b/chromium/chrome/browser/resources/pdf/includes.js
new file mode 100644
index 00000000000..00de804866e
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/includes.js
@@ -0,0 +1,12 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+<include src="../../../../third_party/polymer/platform/platform.js">
+<include src="../../../../third_party/polymer/polymer/polymer.js">
+<include src="html_office/elements/viewer-toolbar/viewer-toolbar.js">
+<include src="html_office/elements/viewer-progress-bar/viewer-progress-bar.js">
+<include src="html_office/elements/viewer-password-screen/viewer-password-screen.js">
+<include src="html_office/elements/viewer-page-indicator/viewer-page-indicator.js">
+<include src="html_office/elements/viewer-error-screen/viewer-error-screen.js">
+<include src="html_office/elements/viewer-button/viewer-button.js">
diff --git a/chromium/chrome/browser/resources/pdf/index.css b/chromium/chrome/browser/resources/pdf/index.css
new file mode 100644
index 00000000000..4186344c81d
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/index.css
@@ -0,0 +1,44 @@
+/* Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+body {
+ background-color: #ccc;
+ margin: 0;
+}
+
+viewer-toolbar {
+ visibility: hidden;
+ z-index: 3;
+}
+
+viewer-page-indicator {
+ visibility: hidden;
+ z-index: 3;
+}
+
+viewer-progress-bar {
+ z-index: 3;
+}
+
+viewer-error-screen {
+ visibility: hidden;
+ z-index: 2;
+}
+
+viewer-password-screen {
+ visibility: hidden;
+ z-index: 2;
+}
+
+#plugin {
+ height: 100%;
+ position: fixed;
+ width: 100%;
+ z-index: 1;
+}
+
+#sizer {
+ position: absolute;
+ z-index: 0;
+}
diff --git a/chromium/chrome/browser/resources/pdf/index.html b/chromium/chrome/browser/resources/pdf/index.html
new file mode 100644
index 00000000000..456313c5ff9
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/index.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html i18n-values="dir:textdirection">
+<head>
+ <meta charset="utf-8">
+ <!-- TODO(raymes): Turn these <include>s into HTML imports once they are
+ fully implemented. At that point includes.js can be removed
+ as the scripts will be pulled in automatically. -->
+ <script src="includes.js"></script>
+ <include src="html_office/elements/viewer-button/viewer-button.html">
+ <include src="html_office/elements/viewer-error-screen/viewer-error-screen.html">
+ <include src="html_office/elements/viewer-page-indicator/viewer-page-indicator.html">
+ <include src="html_office/elements/viewer-password-screen/viewer-password-screen.html">
+ <include src="html_office/elements/viewer-progress-bar/viewer-progress-bar.html">
+ <include src="html_office/elements/viewer-toolbar/viewer-toolbar.html">
+ <link rel="stylesheet" type="text/css" href="index.css">
+</head>
+<body>
+
+<div id="sizer"></div>
+<viewer-password-screen id="password-screen"></viewer-password-screen>
+<viewer-page-indicator id="page-indicator"></viewer-page-indicator>
+<viewer-progress-bar id="progress-bar"></viewer-progress-bar>
+
+<viewer-toolbar id="toolbar">
+ <viewer-button id="fit-to-page-button"
+ assetpath="html_office/elements/viewer-button/"
+ img="button_fit_page.png" latchable>
+ </viewer-button>
+ <viewer-button id="fit-to-width-button"
+ assetpath="html_office/elements/viewer-button/"
+ img="button_fit_width.png" latchable>
+ </viewer-button>
+ <viewer-button id="zoom-in-button"
+ assetpath="html_office/elements/viewer-button/"
+ img="button_zoom_in.png">
+ </viewer-button>
+ <viewer-button id="zoom-out-button"
+ assetpath="html_office/elements/viewer-button/"
+ img="button_zoom_out.png">
+ </viewer-button>
+ <a id="save-button-link" download>
+ <viewer-button id="save-button"
+ assetpath="html_office/elements/viewer-button/"
+ img="button_save.png">
+ </viewer-button>
+ </a>
+ <viewer-button id="print-button"
+ assetpath="html_office/elements/viewer-button/"
+ img="button_print.png">
+ </viewer-button>
+</viewer-toolbar>
+
+<viewer-error-screen id="error-screen"></viewer-error-screen>
+
+</body>
+<script src="pdf.js"></script>
+</html>
diff --git a/chromium/chrome/browser/resources/pdf/manifest.json b/chromium/chrome/browser/resources/pdf/manifest.json
index 07e577aca3a..f97e970ef63 100644
--- a/chromium/chrome/browser/resources/pdf/manifest.json
+++ b/chromium/chrome/browser/resources/pdf/manifest.json
@@ -6,10 +6,13 @@
"version": "1",
"description": "Chrome PDF Viewer",
"offline_enabled": true,
+ "incognito": "split",
"permissions": [
"file://*/",
+ "ftp://*/",
"http://*/",
"https://*/",
+ "chrome://*/",
"streamsPrivate"
],
"background": {
@@ -17,11 +20,10 @@
"transient": true,
"persistent": false
},
- "offline_enabled": true,
"mime_types": [
"application/pdf"
],
"web_accessible_resources": [
- "pdf.html"
+ "index.html"
]
}
diff --git a/chromium/chrome/browser/resources/pdf/pdf.html b/chromium/chrome/browser/resources/pdf/pdf.html
deleted file mode 100644
index 61db52371eb..00000000000
--- a/chromium/chrome/browser/resources/pdf/pdf.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE HTML>
-<html i18n-values="dir:textdirection">
-<head>
- <meta charset="utf-8">
- <style>
- body {
- background-color: #ccc;
- font-family: sans-serif;
- }
- </style>
-</head>
-<body marginwidth="0" marginheight="0" >
-</body>
-<script src="pdf.js" language="javascript" type="text/javascript"></script>
-</html>
diff --git a/chromium/chrome/browser/resources/pdf/pdf.js b/chromium/chrome/browser/resources/pdf/pdf.js
index c2a47f476a4..60384e5db6e 100644
--- a/chromium/chrome/browser/resources/pdf/pdf.js
+++ b/chromium/chrome/browser/resources/pdf/pdf.js
@@ -2,50 +2,486 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-(function() {
+'use strict';
-var plugin;
-var sizer;
+<include src="../../../../ui/webui/resources/js/util.js">
+<include src="pdf_scripting_api.js">
+<include src="viewport.js">
-function onScroll() {
- var coordinates = [window.pageXOffset, window.pageYOffset];
- plugin.postMessage(coordinates);
+/**
+ * @return {number} Width of a scrollbar in pixels
+ */
+function getScrollbarWidth() {
+ var div = document.createElement('div');
+ div.style.visibility = 'hidden';
+ div.style.overflow = 'scroll';
+ div.style.width = '50px';
+ div.style.height = '50px';
+ div.style.position = 'absolute';
+ document.body.appendChild(div);
+ var result = div.offsetWidth - div.clientWidth;
+ div.parentNode.removeChild(div);
+ return result;
}
-function handleMessage(message) {
- if (message.data['type'] == 'document_dimensions') {
- if (sizer.style.height != message.data['document_height'] + 'px') {
- sizer.style.height = message.data['document_height'] + 'px';
- sizer.style.width = message.data['document_width'] + 'px';
- }
+/**
+ * The minimum number of pixels to offset the toolbar by from the bottom and
+ * right side of the screen.
+ */
+PDFViewer.MIN_TOOLBAR_OFFSET = 15;
+
+/**
+ * Creates a new PDFViewer. There should only be one of these objects per
+ * document.
+ */
+function PDFViewer() {
+ this.loaded = false;
+
+ // The sizer element is placed behind the plugin element to cause scrollbars
+ // to be displayed in the window. It is sized according to the document size
+ // of the pdf and zoom level.
+ this.sizer_ = $('sizer');
+ this.toolbar_ = $('toolbar');
+ this.pageIndicator_ = $('page-indicator');
+ this.progressBar_ = $('progress-bar');
+ this.passwordScreen_ = $('password-screen');
+ this.passwordScreen_.addEventListener('password-submitted',
+ this.onPasswordSubmitted_.bind(this));
+ this.errorScreen_ = $('error-screen');
+
+ // Create the viewport.
+ this.viewport_ = new Viewport(window,
+ this.sizer_,
+ this.viewportChangedCallback_.bind(this),
+ getScrollbarWidth());
+
+ // Create the plugin object dynamically so we can set its src. The plugin
+ // element is sized to fill the entire window and is set to be fixed
+ // positioning, acting as a viewport. The plugin renders into this viewport
+ // according to the scroll position of the window.
+ this.plugin_ = document.createElement('object');
+ // NOTE: The plugin's 'id' field must be set to 'plugin' since
+ // 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.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
+ // PDF to load. This is used for print preview in particular.
+ if (chrome.extension.getBackgroundPage &&
+ chrome.extension.getBackgroundPage()) {
+ this.streamDetails =
+ chrome.extension.getBackgroundPage().popStreamDetails();
}
-}
-function load() {
- window.addEventListener('scroll',
- function() { webkitRequestAnimationFrame(onScroll); });
-
- // The pdf location is passed in the document url in the format:
- // http://<.../pdf.html>?<pdf location>.
- var url = window.location.search.substring(1);
- plugin = document.createElement('object');
- plugin.setAttribute('width', '100%');
- plugin.setAttribute('height', '100%');
- plugin.setAttribute('type', 'application/x-google-chrome-pdf');
- plugin.setAttribute('src', url);
- plugin.style.zIndex = '1';
- plugin.style.position = 'fixed';
- plugin.addEventListener('message', handleMessage, false);
- document.body.appendChild(plugin);
-
- sizer = document.createElement('div');
- sizer.style.zIndex = '0';
- sizer.style.position = 'absolute';
- sizer.style.width = '100%';
- sizer.style.height = '100%';
- document.body.appendChild(sizer);
+ if (!this.streamDetails) {
+ // The URL of this page will be of the form
+ // "chrome-extension://<extension id>?<pdf url>". We pull out the <pdf url>
+ // part here.
+ var url = window.location.search.substring(1);
+ this.streamDetails = {
+ streamUrl: url,
+ originalUrl: url,
+ responseHeaders: ''
+ };
+ }
+
+ this.plugin_.setAttribute('src', this.streamDetails.originalUrl);
+ this.plugin_.setAttribute('stream-url', this.streamDetails.streamUrl);
+ var headers = '';
+ for (var header in this.streamDetails.responseHeaders) {
+ headers += header + ': ' +
+ this.streamDetails.responseHeaders[header] + '\n';
+ }
+ this.plugin_.setAttribute('headers', headers);
+
+ if (window.top == window)
+ this.plugin_.setAttribute('full-frame', '');
+ document.body.appendChild(this.plugin_);
+
+ // Setup the button event listeners.
+ $('fit-to-width-button').addEventListener('click',
+ this.viewport_.fitToWidth.bind(this.viewport_));
+ $('fit-to-page-button').addEventListener('click',
+ this.viewport_.fitToPage.bind(this.viewport_));
+ $('zoom-in-button').addEventListener('click',
+ this.viewport_.zoomIn.bind(this.viewport_));
+ $('zoom-out-button').addEventListener('click',
+ this.viewport_.zoomOut.bind(this.viewport_));
+ $('save-button-link').href = this.streamDetails.originalUrl;
+ $('print-button').addEventListener('click', this.print_.bind(this));
+
+ // Setup the keyboard event listener.
+ document.onkeydown = this.handleKeyEvent_.bind(this);
}
-load();
+PDFViewer.prototype = {
+ /**
+ * @private
+ * Handle key events. These may come from the user directly or via the
+ * scripting API.
+ * @param {KeyboardEvent} e the event to handle.
+ */
+ handleKeyEvent_: function(e) {
+ var position = this.viewport_.position;
+ // Certain scroll events may be sent from outside of the extension.
+ var fromScriptingAPI = e.type == 'scriptingKeypress';
+
+ switch (e.keyCode) {
+ case 33: // Page up key.
+ // Go to the previous page if we are fit-to-page.
+ if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
+ this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
+ // Since we do the movement of the page.
+ e.preventDefault();
+ } else if (fromScriptingAPI) {
+ position.y -= this.viewport.size.height;
+ this.viewport.position = position;
+ }
+ return;
+ case 34: // Page down key.
+ // Go to the next page if we are fit-to-page.
+ if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
+ this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
+ // Since we do the movement of the page.
+ e.preventDefault();
+ } else if (fromScriptingAPI) {
+ position.y += this.viewport.size.height;
+ this.viewport.position = position;
+ }
+ return;
+ case 37: // Left arrow key.
+ // Go to the previous page if there are no horizontal scrollbars.
+ if (!this.viewport_.documentHasScrollbars().x) {
+ this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
+ // Since we do the movement of the page.
+ e.preventDefault();
+ } else if (fromScriptingAPI) {
+ position.x -= Viewport.SCROLL_INCREMENT;
+ this.viewport.position = position;
+ }
+ return;
+ case 38: // Up arrow key.
+ if (fromScriptingAPI) {
+ position.y -= Viewport.SCROLL_INCREMENT;
+ this.viewport.position = position;
+ }
+ return;
+ case 39: // Right arrow key.
+ // Go to the next page if there are no horizontal scrollbars.
+ if (!this.viewport_.documentHasScrollbars().x) {
+ this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
+ // Since we do the movement of the page.
+ e.preventDefault();
+ } else if (fromScriptingAPI) {
+ position.x += Viewport.SCROLL_INCREMENT;
+ this.viewport.position = position;
+ }
+ return;
+ case 40: // Down arrow key.
+ if (fromScriptingAPI) {
+ position.y += Viewport.SCROLL_INCREMENT;
+ this.viewport.position = position;
+ }
+ return;
+ case 187: // +/= key.
+ case 107: // Numpad + key.
+ if (e.ctrlKey || e.metaKey) {
+ this.viewport_.zoomIn();
+ // Since we do the zooming of the page.
+ e.preventDefault();
+ }
+ return;
+ case 189: // -/_ key.
+ case 109: // Numpad - key.
+ if (e.ctrlKey || e.metaKey) {
+ this.viewport_.zoomOut();
+ // Since we do the zooming of the page.
+ e.preventDefault();
+ }
+ return;
+ case 83: // s key.
+ if (e.ctrlKey || e.metaKey) {
+ // Simulate a click on the button so that the <a download ...>
+ // attribute is used.
+ $('save-button-link').click();
+ // Since we do the saving of the page.
+ e.preventDefault();
+ }
+ return;
+ case 80: // p key.
+ if (e.ctrlKey || e.metaKey) {
+ this.print_();
+ // Since we do the printing of the page.
+ e.preventDefault();
+ }
+ return;
+ }
+ },
+
+ /**
+ * @private
+ * Notify the plugin to print.
+ */
+ print_: function() {
+ this.plugin_.postMessage({
+ type: 'print',
+ });
+ },
+
+ /**
+ * @private
+ * Update the loading progress of the document in response to a progress
+ * message being received from the plugin.
+ * @param {number} progress the progress as a percentage.
+ */
+ updateProgress_: function(progress) {
+ this.progressBar_.progress = progress;
+ if (progress == -1) {
+ // Document load failed.
+ this.errorScreen_.style.visibility = 'visible';
+ this.sizer_.style.display = 'none';
+ this.toolbar_.style.visibility = 'hidden';
+ if (this.passwordScreen_.active) {
+ this.passwordScreen_.deny();
+ this.passwordScreen_.active = false;
+ }
+ } else if (progress == 100) {
+ // Document load complete.
+ this.loaded = true;
+ var loadEvent = new Event('pdfload');
+ window.dispatchEvent(loadEvent);
+ this.sendScriptingMessage_({
+ type: 'documentLoaded'
+ });
+ if (this.lastViewportPosition_)
+ this.viewport_.position = this.lastViewportPosition_;
+ }
+ },
+
+ /**
+ * @private
+ * An event handler for handling password-submitted events. These are fired
+ * when an event is entered into the password screen.
+ * @param {Object} event a password-submitted event.
+ */
+ onPasswordSubmitted_: function(event) {
+ this.plugin_.postMessage({
+ type: 'getPasswordComplete',
+ password: event.detail.password
+ });
+ },
+
+ /**
+ * @private
+ * An event handler for handling message events received from the plugin.
+ * @param {MessageObject} message a message event.
+ */
+ handlePluginMessage_: function(message) {
+ switch (message.data.type.toString()) {
+ case 'documentDimensions':
+ this.documentDimensions_ = message.data;
+ this.viewport_.setDocumentDimensions(this.documentDimensions_);
+ this.toolbar_.style.visibility = 'visible';
+ // If we received the document dimensions, the password was good so we
+ // can dismiss the password screen.
+ if (this.passwordScreen_.active)
+ this.passwordScreen_.accept();
+
+ this.pageIndicator_.initialFadeIn();
+ this.toolbar_.initialFadeIn();
+ break;
+ case 'email':
+ var href = 'mailto:' + message.data.to + '?cc=' + message.data.cc +
+ '&bcc=' + message.data.bcc + '&subject=' + message.data.subject +
+ '&body=' + message.data.body;
+ var w = window.open(href, '_blank', 'width=1,height=1');
+ if (w)
+ w.close();
+ break;
+ case 'getAccessibilityJSONReply':
+ this.sendScriptingMessage_(message.data);
+ break;
+ case 'getPassword':
+ // If the password screen isn't up, put it up. Otherwise we're
+ // responding to an incorrect password so deny it.
+ if (!this.passwordScreen_.active)
+ this.passwordScreen_.active = true;
+ else
+ this.passwordScreen_.deny();
+ break;
+ case 'goToPage':
+ this.viewport_.goToPage(message.data.page);
+ break;
+ case 'loadProgress':
+ this.updateProgress_(message.data.progress);
+ break;
+ case 'navigate':
+ if (message.data.newTab)
+ window.open(message.data.url);
+ else
+ window.location.href = message.data.url;
+ break;
+ case 'setScrollPosition':
+ var position = this.viewport_.position;
+ if (message.data.x != undefined)
+ position.x = message.data.x;
+ if (message.data.y != undefined)
+ position.y = message.data.y;
+ this.viewport_.position = position;
+ break;
+ case 'setTranslatedStrings':
+ this.passwordScreen_.text = message.data.getPasswordString;
+ this.progressBar_.text = message.data.loadingString;
+ this.errorScreen_.text = message.data.loadFailedString;
+ break;
+ case 'cancelStreamUrl':
+ chrome.streamsPrivate.abort(this.streamDetails.streamUrl);
+ break;
+ }
+ },
+
+ /**
+ * @private
+ * A callback that's called when the viewport changes.
+ */
+ viewportChangedCallback_: function() {
+ if (!this.documentDimensions_)
+ return;
+
+ // Update the buttons selected.
+ $('fit-to-page-button').classList.remove('polymer-selected');
+ $('fit-to-width-button').classList.remove('polymer-selected');
+ if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
+ $('fit-to-page-button').classList.add('polymer-selected');
+ } else if (this.viewport_.fittingType ==
+ Viewport.FittingType.FIT_TO_WIDTH) {
+ $('fit-to-width-button').classList.add('polymer-selected');
+ }
+
+ var hasScrollbars = this.viewport_.documentHasScrollbars();
+ var scrollbarWidth = this.viewport_.scrollbarWidth;
+ // Offset the toolbar position so that it doesn't move if scrollbars appear.
+ var toolbarRight = Math.max(PDFViewer.MIN_TOOLBAR_OFFSET, scrollbarWidth);
+ var toolbarBottom = Math.max(PDFViewer.MIN_TOOLBAR_OFFSET, scrollbarWidth);
+ if (hasScrollbars.vertical)
+ toolbarRight -= scrollbarWidth;
+ if (hasScrollbars.horizontal)
+ toolbarBottom -= scrollbarWidth;
+ this.toolbar_.style.right = toolbarRight + 'px';
+ this.toolbar_.style.bottom = toolbarBottom + 'px';
+
+ // Update the page indicator.
+ var visiblePage = this.viewport_.getMostVisiblePage();
+ this.pageIndicator_.index = visiblePage;
+ if (this.documentDimensions_.pageDimensions.length > 1 &&
+ hasScrollbars.vertical) {
+ this.pageIndicator_.style.visibility = 'visible';
+ } else {
+ this.pageIndicator_.style.visibility = 'hidden';
+ }
+
+ var position = this.viewport_.position;
+ var zoom = this.viewport_.zoom;
+ // Notify the plugin of the viewport change.
+ this.plugin_.postMessage({
+ type: 'viewport',
+ zoom: zoom,
+ 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,
+ });
+ },
+
+ /**
+ * @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.
+ */
+ handleScriptingMessage_: function(message) {
+ switch (message.data.type.toString()) {
+ case 'getAccessibilityJSON':
+ case 'loadPreviewPage':
+ this.plugin_.postMessage(message.data);
+ break;
+ 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 'sendKeyEvent':
+ var e = document.createEvent('Event');
+ e.initEvent('scriptingKeypress');
+ e.keyCode = message.data.keyCode;
+ this.handleKeyEvent_(e);
+ break;
+ }
+
+ },
+
+ /**
+ * @private
+ * Send a scripting message outside the extension (typically to
+ * PDFScriptingAPI in a page containing the extension).
+ * @param {Object} message the message to send.
+ */
+ sendScriptingMessage_: function(message) {
+ window.parent.postMessage(message, '*');
+ },
+
+ /**
+ * @type {Viewport} the viewport of the PDF viewer.
+ */
+ get viewport() {
+ return this.viewport_;
+ }
+};
-})();
+var viewer = new PDFViewer();
diff --git a/chromium/chrome/browser/resources/pdf/pdf_extension_test.cc b/chromium/chrome/browser/resources/pdf/pdf_extension_test.cc
new file mode 100644
index 00000000000..73b6b807433
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/pdf_extension_test.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/manifest_handlers/mime_types_handler.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test_utils.h"
+#include "grit/browser_resources.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+class PDFExtensionTest : public ExtensionApiTest {
+ public:
+ virtual ~PDFExtensionTest() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ ExtensionApiTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kOutOfProcessPdf);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ ExtensionApiTest::SetUpOnMainThread();
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ }
+
+
+ virtual void CleanUpOnMainThread() OVERRIDE {
+ ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+ ExtensionApiTest::CleanUpOnMainThread();
+ }
+
+ void RunTestsInFile(std::string filename, bool requiresPlugin) {
+ base::FilePath pdf_plugin_src;
+ PathService::Get(base::DIR_SOURCE_ROOT, &pdf_plugin_src);
+ pdf_plugin_src = pdf_plugin_src.AppendASCII("pdf");
+ if (requiresPlugin && !base::DirectoryExists(pdf_plugin_src)) {
+ LOG(WARNING) << "Not running " << filename <<
+ " because it requires the PDF plugin which is not available.";
+ return;
+ }
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ profile())->extension_service();
+ service->component_loader()->Add(IDR_PDF_MANIFEST,
+ base::FilePath(FILE_PATH_LITERAL("pdf")));
+ const extensions::Extension* extension =
+ service->extensions()->GetByID("mhjfbmdgcfjbbpaeojofohoefgiehjai");
+ ASSERT_TRUE(extension);
+ ASSERT_TRUE(MimeTypesHandler::GetHandler(
+ extension)->CanHandleMIMEType("application/pdf"));
+
+ ResultCatcher catcher;
+
+ GURL url(embedded_test_server()->GetURL("/pdf/test.pdf"));
+ GURL extension_url(
+ "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?" +
+ url.spec());
+ ui_test_utils::NavigateToURL(browser(), extension_url);
+ content::WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ content::WaitForLoadStop(contents);
+
+ base::FilePath test_data_dir;
+ PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir);
+ test_data_dir = test_data_dir.Append(
+ FILE_PATH_LITERAL("chrome/test/data/pdf"));
+ test_data_dir = test_data_dir.AppendASCII(filename);
+
+ std::string test_js;
+ ASSERT_TRUE(base::ReadFileToString(test_data_dir, &test_js));
+ ASSERT_TRUE(content::ExecuteScript(contents, test_js));
+
+ if (!catcher.GetNextResult())
+ FAIL() << catcher.message();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Basic) {
+ RunTestsInFile("basic_test.js", false);
+}
+
+// TODO(raymes): This fails with component builds on linux because the plugin
+// plugin crashes due to something related to how the plugin DLL is
+// compiled/linked. crbug.com/386436.
+#if defined(LINUX) && defined(COMPONENT_BUILD)
+#define MAYBE_BasicPlugin DISABLED_BasicPlugin
+#else
+#define MAYBE_BasicPlugin BasicPlugin
+#endif
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, MAYBE_BasicPlugin) {
+ RunTestsInFile("basic_plugin_test.js", true);
+}
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Viewport) {
+ RunTestsInFile("viewport_test.js", false);
+}
diff --git a/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js b/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js
new file mode 100644
index 00000000000..2b98c7c7973
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js
@@ -0,0 +1,181 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * 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 {Window} window the window of the page containing the pdf viewer.
+ * @param {string} extensionUrl the url of the PDF extension.
+ */
+function PDFScriptingAPI(window, extensionUrl) {
+ this.extensionUrl_ = extensionUrl;
+ this.messageQueue_ = [];
+ window.addEventListener('message', function(event) {
+ if (event.origin != this.extensionUrl_) {
+ console.error('Received message that was not from the extension: ' +
+ event);
+ return;
+ }
+ switch (event.data.type) {
+ case 'readyToReceive':
+ this.setDestinationWindow(event.source);
+ break;
+ 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':
+ if (this.loadCallback_)
+ this.loadCallback_();
+ break;
+ case 'getAccessibilityJSONReply':
+ if (this.accessibilityCallback_) {
+ this.accessibilityCallback_(event.data.json);
+ this.accessibilityCallback_ = null;
+ }
+ break;
+ }
+ }.bind(this), false);
+}
+
+PDFScriptingAPI.prototype = {
+ /**
+ * @private
+ * Send a message to the extension. If we try to send messages prior to the
+ * extension being ready to receive messages (i.e. before it has finished
+ * loading) we queue up the messages and flush them later.
+ * @param {Object} the message to send.
+ */
+ sendMessage_: function(message) {
+ if (!this.pdfWindow_) {
+ this.messageQueue_.push(message);
+ return;
+ }
+
+ this.pdfWindow_.postMessage(message, this.extensionUrl_);
+ },
+
+ /**
+ * Sets the destination window containing the PDF viewer. This will be called
+ * when a 'readyToReceive' message is received from the PDF viewer or it can
+ * be called during tests. It then flushes any pending messages to the window.
+ * @param {Window} pdfWindow the window containing the PDF viewer.
+ */
+ setDestinationWindow: function(pdfWindow) {
+ this.pdfWindow_ = pdfWindow;
+ while (this.messageQueue_.length != 0) {
+ this.pdfWindow_.postMessage(this.messageQueue_.shift(),
+ this.extensionUrl_);
+ }
+ },
+
+ /**
+ * Sets the callback which will be run when the PDF viewport changes.
+ * @param {Function} callback the callback to be called.
+ */
+ setViewportChangedCallback: function(callback) {
+ this.viewportChangedCallback_ = callback;
+ },
+
+ /**
+ * Sets the callback which will be run when the PDF document has finished
+ * loading.
+ * @param {Function} callback the callback to be called.
+ */
+ setLoadCallback: function(callback) {
+ this.loadCallback_ = callback;
+ },
+
+ /**
+ * Resets the PDF viewer into print preview mode.
+ * @param {string} url the url of the PDF to load.
+ * @param {boolean} grayscale whether or not to display the PDF in grayscale.
+ * @param {Array.<number>} pageNumbers an array of the page numbers.
+ * @param {boolean} modifiable whether or not the document is modifiable.
+ */
+ resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) {
+ this.sendMessage_({
+ type: 'resetPrintPreviewMode',
+ url: url,
+ grayscale: grayscale,
+ pageNumbers: pageNumbers,
+ modifiable: modifiable
+ });
+ },
+
+ /**
+ * 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.
+ */
+ loadPreviewPage: function(url, index) {
+ this.sendMessage_({
+ type: 'loadPreviewPage',
+ url: url,
+ index: index
+ });
+ },
+
+ /**
+ * Get accessibility JSON for the document.
+ * @param {Function} callback a callback to be called with the accessibility
+ * json that has been retrieved.
+ * @param {number} [page] the 0-indexed page number to get accessibility data
+ * for. If this is not provided, data about the entire document is
+ * returned.
+ * @return {boolean} true if the function is successful, false if there is an
+ * outstanding request for accessibility data that has not been answered.
+ */
+ getAccessibilityJSON: function(callback, page) {
+ if (this.accessibilityCallback_)
+ return false;
+ this.accessibilityCallback_ = callback;
+ var message = {
+ type: 'getAccessibilityJSON',
+ };
+ if (page || page == 0)
+ message.page = page;
+ this.sendMessage_(message);
+ return true;
+ },
+
+ /**
+ * Send a key event to the extension.
+ * @param {number} keyCode the key code to send to the extension.
+ */
+ sendKeyEvent: function(keyCode) {
+ this.sendMessage_({
+ type: 'sendKeyEvent',
+ keyCode: keyCode
+ });
+ },
+};
+
+/**
+ * Creates a PDF viewer with a scripting interface. This is basically 1) an
+ * iframe which is navigated to the PDF viewer extension and 2) a scripting
+ * interface which provides access to various features of the viewer for use
+ * by print preview and accessbility.
+ * @param {string} src the source URL of the PDF to load initially.
+ * @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
+ */
+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 PDFScriptingAPI(window, EXTENSION_URL);
+
+ // Add the functions to the iframe so that they can be called directly.
+ iframe.setViewportChangedCallback =
+ client.setViewportChangedCallback.bind(client);
+ iframe.setLoadCallback = client.setLoadCallback.bind(client);
+ iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client);
+ iframe.loadPreviewPage = client.loadPreviewPage.bind(client);
+ iframe.sendKeyEvent = client.sendKeyEvent.bind(client);
+ return iframe;
+}
diff --git a/chromium/chrome/browser/resources/pdf/polymer_loader.js b/chromium/chrome/browser/resources/pdf/polymer_loader.js
new file mode 100644
index 00000000000..d6199ef2dd4
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/polymer_loader.js
@@ -0,0 +1,12 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+// This is necessary because polymer currently doesn't handle the configuration
+// where native Custom Elements are available but native Shadow DOM is not.
+// TODO(bshe): remove this line when polymer supports the configuration.
+document.registerElement = undefined;
+
+<include src="../../../../third_party/polymer/platform/platform.js"></include>
+})();
diff --git a/chromium/chrome/browser/resources/pdf/viewport.js b/chromium/chrome/browser/resources/pdf/viewport.js
new file mode 100644
index 00000000000..7da0143bacb
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/viewport.js
@@ -0,0 +1,470 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Returns the area of the intersection of two rectangles.
+ * @param {Object} rect1 the first rect
+ * @param {Object} rect2 the second rect
+ * @return {number} the area of the intersection of the rects
+ */
+function getIntersectionArea(rect1, rect2) {
+ var xOverlap = Math.max(0,
+ Math.min(rect1.x + rect1.width, rect2.x + rect2.width) -
+ Math.max(rect1.x, rect2.x));
+ var yOverlap = Math.max(0,
+ Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
+ Math.max(rect1.y, rect2.y));
+ return xOverlap * yOverlap;
+}
+
+/**
+ * Create a new viewport.
+ * @param {Window} window the window
+ * @param {Object} sizer is the element which represents the size of the
+ * document in the viewport
+ * @param {Function} viewportChangedCallback is run when the viewport changes
+ * @param {number} scrollbarWidth the width of scrollbars on the page
+ */
+function Viewport(window,
+ sizer,
+ viewportChangedCallback,
+ scrollbarWidth) {
+ this.window_ = window;
+ this.sizer_ = sizer;
+ this.viewportChangedCallback_ = viewportChangedCallback;
+ this.zoom_ = 1;
+ this.documentDimensions_ = null;
+ this.pageDimensions_ = [];
+ this.scrollbarWidth_ = scrollbarWidth;
+ this.fittingType_ = Viewport.FittingType.NONE;
+
+ window.addEventListener('scroll', this.updateViewport_.bind(this));
+ window.addEventListener('resize', this.resize_.bind(this));
+}
+
+/**
+ * Enumeration of page fitting types.
+ * @enum {string}
+ */
+Viewport.FittingType = {
+ NONE: 'none',
+ FIT_TO_PAGE: 'fit-to-page',
+ FIT_TO_WIDTH: 'fit-to-width'
+};
+
+/**
+ * The increment to scroll a page by in pixels when up/down/left/right arrow
+ * keys are pressed. Usually we just let the browser handle scrolling on the
+ * window when these keys are pressed but in certain cases we need to simulate
+ * these events.
+ */
+Viewport.SCROLL_INCREMENT = 40;
+
+/**
+ * Predefined zoom factors to be used when zooming in/out. These are in
+ * ascending order.
+ */
+Viewport.ZOOM_FACTORS = [0.25, 0.333, 0.5, 0.666, 0.75, 0.9, 1,
+ 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5];
+
+/**
+ * The width of the page shadow around pages in pixels.
+ */
+Viewport.PAGE_SHADOW = {top: 3, bottom: 7, left: 5, right: 5};
+
+Viewport.prototype = {
+ /**
+ * @private
+ * Returns true if the document needs scrollbars at the given zoom level.
+ * @param {number} zoom compute whether scrollbars are needed at this zoom
+ * @return {Object} with 'horizontal' and 'vertical' keys which map to bool
+ * values indicating if the horizontal and vertical scrollbars are needed
+ * respectively.
+ */
+ documentNeedsScrollbars_: function(zoom) {
+ var documentWidth = this.documentDimensions_.width * zoom;
+ var documentHeight = this.documentDimensions_.height * zoom;
+ return {
+ horizontal: documentWidth > this.window_.innerWidth,
+ vertical: documentHeight > this.window_.innerHeight
+ };
+ },
+
+ /**
+ * Returns true if the document needs scrollbars at the current zoom level.
+ * @return {Object} with 'x' and 'y' keys which map to bool values
+ * indicating if the horizontal and vertical scrollbars are needed
+ * respectively.
+ */
+ documentHasScrollbars: function() {
+ return this.documentNeedsScrollbars_(this.zoom_);
+ },
+
+ /**
+ * @private
+ * Helper function called when the zoomed document size changes.
+ */
+ contentSizeChanged_: function() {
+ if (this.documentDimensions_) {
+ this.sizer_.style.width =
+ this.documentDimensions_.width * this.zoom_ + 'px';
+ this.sizer_.style.height =
+ this.documentDimensions_.height * this.zoom_ + 'px';
+ }
+ },
+
+ /**
+ * @private
+ * Called when the viewport should be updated.
+ */
+ updateViewport_: function() {
+ this.viewportChangedCallback_();
+ },
+
+ /**
+ * @private
+ * Called when the viewport size changes.
+ */
+ resize_: function() {
+ if (this.fittingType_ == Viewport.FittingType.FIT_TO_PAGE)
+ this.fitToPage();
+ else if (this.fittingType_ == Viewport.FittingType.FIT_TO_WIDTH)
+ this.fitToWidth();
+ else
+ this.updateViewport_();
+ },
+
+ /**
+ * @type {Object} the scroll position of the viewport.
+ */
+ get position() {
+ return {
+ x: this.window_.pageXOffset,
+ y: this.window_.pageYOffset
+ };
+ },
+
+ /**
+ * Scroll the viewport to the specified position.
+ * @type {Object} position the position to scroll to.
+ */
+ set position(position) {
+ this.window_.scrollTo(position.x, position.y);
+ },
+
+ /**
+ * @type {Object} the size of the viewport excluding scrollbars.
+ */
+ get size() {
+ var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
+ var scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0;
+ var scrollbarHeight = needsScrollbars.horizontal ? this.scrollbarWidth_ : 0;
+ return {
+ width: this.window_.innerWidth - scrollbarWidth,
+ height: this.window_.innerHeight - scrollbarHeight
+ };
+ },
+
+ /**
+ * @type {number} the zoom level of the viewport.
+ */
+ get zoom() {
+ return this.zoom_;
+ },
+
+ /**
+ * @private
+ * Sets the zoom of the viewport.
+ * @param {number} newZoom the zoom level to zoom to.
+ */
+ setZoom_: function(newZoom) {
+ var oldZoom = this.zoom_;
+ this.zoom_ = newZoom;
+ // Record the scroll position (relative to the middle of the window).
+ var currentScrollPos = [
+ (this.window_.pageXOffset + this.window_.innerWidth / 2) / oldZoom,
+ (this.window_.pageYOffset + this.window_.innerHeight / 2) / oldZoom
+ ];
+ this.contentSizeChanged_();
+ // Scroll to the scaled scroll position.
+ this.window_.scrollTo(
+ currentScrollPos[0] * newZoom - this.window_.innerWidth / 2,
+ currentScrollPos[1] * newZoom - this.window_.innerHeight / 2);
+ },
+
+ /**
+ * @type {number} the width of scrollbars in the viewport in pixels.
+ */
+ get scrollbarWidth() {
+ return this.scrollbarWidth_;
+ },
+
+ /**
+ * @type {Viewport.FittingType} the fitting type the viewport is currently in.
+ */
+ get fittingType() {
+ return this.fittingType_;
+ },
+
+ /**
+ * @private
+ * @param {integer} y the y-coordinate to get the page at.
+ * @return {integer} the index of a page overlapping the given y-coordinate.
+ */
+ getPageAtY_: function(y) {
+ var min = 0;
+ var max = this.pageDimensions_.length - 1;
+ while (max >= min) {
+ var page = Math.floor(min + ((max - min) / 2));
+ // There might be a gap between the pages, in which case use the bottom
+ // of the previous page as the top for finding the page.
+ var top = 0;
+ if (page > 0) {
+ top = this.pageDimensions_[page - 1].y +
+ this.pageDimensions_[page - 1].height;
+ }
+ var bottom = this.pageDimensions_[page].y +
+ this.pageDimensions_[page].height;
+
+ if (top <= y && bottom > y)
+ return page;
+ else if (top > y)
+ max = page - 1;
+ else
+ min = page + 1;
+ }
+ return 0;
+ },
+
+ /**
+ * Returns the page with the most pixels in the current viewport.
+ * @return {int} the index of the most visible page.
+ */
+ getMostVisiblePage: function() {
+ var firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom_);
+ var mostVisiblePage = {number: 0, area: 0};
+ var viewportRect = {
+ x: this.position.x / this.zoom_,
+ y: this.position.y / this.zoom_,
+ width: this.size.width / this.zoom_,
+ height: this.size.height / this.zoom_
+ };
+ for (var i = firstVisiblePage; i < this.pageDimensions_.length; i++) {
+ var area = getIntersectionArea(this.pageDimensions_[i],
+ viewportRect);
+ // If we hit a page with 0 area overlap, we must have gone past the
+ // pages visible in the viewport so we can break.
+ if (area == 0)
+ break;
+ if (area > mostVisiblePage.area) {
+ mostVisiblePage.area = area;
+ mostVisiblePage.number = i;
+ }
+ }
+ return mostVisiblePage.number;
+ },
+
+ /**
+ * @private
+ * Compute the zoom level for fit-to-page or fit-to-width. |pageDimensions| is
+ * the dimensions for a given page and if |widthOnly| is true, it indicates
+ * that fit-to-page zoom should be computed rather than fit-to-page.
+ * @param {Object} pageDimensions the dimensions of a given page
+ * @param {boolean} widthOnly a bool indicating whether fit-to-page or
+ * fit-to-width should be computed.
+ * @return {number} the zoom to use
+ */
+ computeFittingZoom_: function(pageDimensions, widthOnly) {
+ // First compute the zoom without scrollbars.
+ var zoomWidth = this.window_.innerWidth / pageDimensions.width;
+ var zoom;
+ if (widthOnly) {
+ zoom = zoomWidth;
+ } else {
+ var zoomHeight = this.window_.innerHeight / pageDimensions.height;
+ zoom = Math.min(zoomWidth, zoomHeight);
+ }
+ // Check if there needs to be any scrollbars.
+ var needsScrollbars = this.documentNeedsScrollbars_(zoom);
+
+ // If the document fits, just return the zoom.
+ if (!needsScrollbars.horizontal && !needsScrollbars.vertical)
+ return zoom;
+
+ var zoomedDimensions = {
+ width: this.documentDimensions_.width * zoom,
+ height: this.documentDimensions_.height * zoom
+ };
+
+ // Check if adding a scrollbar will result in needing the other scrollbar.
+ var scrollbarWidth = this.scrollbarWidth_;
+ if (needsScrollbars.horizontal &&
+ zoomedDimensions.height > this.window_.innerHeight - scrollbarWidth) {
+ needsScrollbars.vertical = true;
+ }
+ if (needsScrollbars.vertical &&
+ zoomedDimensions.width > this.window_.innerWidth - scrollbarWidth) {
+ needsScrollbars.horizontal = true;
+ }
+
+ // Compute available window space.
+ var windowWithScrollbars = {
+ width: this.window_.innerWidth,
+ height: this.window_.innerHeight
+ };
+ if (needsScrollbars.horizontal)
+ windowWithScrollbars.height -= scrollbarWidth;
+ if (needsScrollbars.vertical)
+ windowWithScrollbars.width -= scrollbarWidth;
+
+ // Recompute the zoom.
+ zoomWidth = windowWithScrollbars.width / pageDimensions.width;
+ if (widthOnly) {
+ zoom = zoomWidth;
+ } else {
+ var zoomHeight = windowWithScrollbars.height / pageDimensions.height;
+ zoom = Math.min(zoomWidth, zoomHeight);
+ }
+ return zoom;
+ },
+
+ /**
+ * Zoom the viewport so that the page-width consumes the entire viewport.
+ */
+ fitToWidth: function() {
+ this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH;
+ if (!this.documentDimensions_)
+ return;
+ // Track the last y-position so we stay at the same position after zooming.
+ var oldY = this.window_.pageYOffset / this.zoom_;
+ // When computing fit-to-width, the maximum width of a page in the document
+ // is used, which is equal to the size of the document width.
+ this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true));
+ var page = this.getMostVisiblePage();
+ this.window_.scrollTo(0, oldY * this.zoom_);
+ this.updateViewport_();
+ },
+
+ /**
+ * Zoom the viewport so that a page consumes the entire viewport. Also scrolls
+ * to the top of the most visible page.
+ */
+ fitToPage: function() {
+ this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE;
+ if (!this.documentDimensions_)
+ return;
+ var page = this.getMostVisiblePage();
+ this.setZoom_(this.computeFittingZoom_(this.pageDimensions_[page], false));
+ // Center the document in the page by scrolling by the amount of empty
+ // space to the left of the document.
+ var xOffset =
+ (this.documentDimensions_.width - this.pageDimensions_[page].width) *
+ this.zoom_ / 2;
+ this.window_.scrollTo(xOffset,
+ this.pageDimensions_[page].y * this.zoom_);
+ this.updateViewport_();
+ },
+
+ /**
+ * Zoom out to the next predefined zoom level.
+ */
+ zoomOut: function() {
+ this.fittingType_ = Viewport.FittingType.NONE;
+ var nextZoom = Viewport.ZOOM_FACTORS[0];
+ for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) {
+ if (Viewport.ZOOM_FACTORS[i] < this.zoom_)
+ nextZoom = Viewport.ZOOM_FACTORS[i];
+ }
+ this.setZoom_(nextZoom);
+ this.updateViewport_();
+ },
+
+ /**
+ * Zoom in to the next predefined zoom level.
+ */
+ zoomIn: function() {
+ this.fittingType_ = Viewport.FittingType.NONE;
+ var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1];
+ for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) {
+ if (Viewport.ZOOM_FACTORS[i] > this.zoom_)
+ nextZoom = Viewport.ZOOM_FACTORS[i];
+ }
+ this.setZoom_(nextZoom);
+ this.updateViewport_();
+ },
+
+ /**
+ * Go to the given page index.
+ * @param {number} page the index of the page to go to.
+ */
+ goToPage: function(page) {
+ if (this.pageDimensions_.length == 0)
+ return;
+ if (page < 0)
+ page = 0;
+ if (page >= this.pageDimensions_.length)
+ page = this.pageDimensions_.length - 1;
+ var dimensions = this.pageDimensions_[page];
+ this.window_.scrollTo(dimensions.x * this.zoom_, dimensions.y * this.zoom_);
+ },
+
+ /**
+ * Set the dimensions of the document.
+ * @param {Object} documentDimensions the dimensions of the document
+ */
+ setDocumentDimensions: function(documentDimensions) {
+ var initialDimensions = !this.documentDimensions_;
+ this.documentDimensions_ = documentDimensions;
+ this.pageDimensions_ = this.documentDimensions_.pageDimensions;
+ if (initialDimensions) {
+ this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true));
+ if (this.zoom_ > 1)
+ this.setZoom_(1);
+ this.window_.scrollTo(0, 0);
+ }
+ this.contentSizeChanged_();
+ this.resize_();
+ },
+
+ /**
+ * Get the coordinates of the page contents (excluding the page shadow)
+ * relative to the screen.
+ * @param {number} page the index of the page to get the rect for.
+ * @return {Object} a rect representing the page in screen coordinates.
+ */
+ getPageScreenRect: function(page) {
+ if (page >= this.pageDimensions_.length)
+ page = this.pageDimensions_.length - 1;
+
+ var pageDimensions = this.pageDimensions_[page];
+
+ // Compute the page dimensions minus the shadows.
+ var insetDimensions = {
+ x: pageDimensions.x + Viewport.PAGE_SHADOW.left,
+ y: pageDimensions.y + Viewport.PAGE_SHADOW.top,
+ width: pageDimensions.width - Viewport.PAGE_SHADOW.left -
+ Viewport.PAGE_SHADOW.right,
+ height: pageDimensions.height - Viewport.PAGE_SHADOW.top -
+ Viewport.PAGE_SHADOW.bottom
+ };
+
+ // Compute the x-coordinate of the page within the document.
+ // TODO(raymes): This should really be set when the PDF plugin passes the
+ // page coordinates, but it isn't yet.
+ var x = (this.documentDimensions_.width - pageDimensions.width) / 2 +
+ Viewport.PAGE_SHADOW.left;
+ // Compute the space on the left of the document if the document fits
+ // completely in the screen.
+ var spaceOnLeft = (this.size.width -
+ this.documentDimensions_.width * this.zoom_) / 2;
+ spaceOnLeft = Math.max(spaceOnLeft, 0);
+
+ return {
+ x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset,
+ y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset,
+ width: insetDimensions.width * this.zoom_,
+ height: insetDimensions.height * this.zoom_
+ };
+ }
+};