summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/pdf
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-16 09:59:13 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-20 10:28:53 +0000
commit6c11fb357ec39bf087b8b632e2b1e375aef1b38b (patch)
treec8315530db18a8ee566521c39ab8a6af4f72bc03 /chromium/chrome/browser/resources/pdf
parent3ffaed019d0772e59d6cdb2d0d32fe4834c31f72 (diff)
BASELINE: Update Chromium to 74.0.3729.159
Change-Id: I8d2497da544c275415aedd94dd25328d555de811 Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/chrome/browser/resources/pdf')
-rw-r--r--chromium/chrome/browser/resources/pdf/BUILD.gn32
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/icons.html8
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn18
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html25
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js29
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn7
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js51
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html1
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js44
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html104
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js45
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html12
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js53
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html49
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js1
-rw-r--r--chromium/chrome/browser/resources/pdf/index.html7
-rw-r--r--chromium/chrome/browser/resources/pdf/ink/BUILD.gn5
-rw-r--r--chromium/chrome/browser/resources/pdf/ink/ink_api.js64
-rw-r--r--chromium/chrome/browser/resources/pdf/metrics.js360
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf_viewer.js196
-rw-r--r--chromium/chrome/browser/resources/pdf/viewport.js456
-rw-r--r--chromium/chrome/browser/resources/pdf/viewport_interface.js136
22 files changed, 1096 insertions, 607 deletions
diff --git a/chromium/chrome/browser/resources/pdf/BUILD.gn b/chromium/chrome/browser/resources/pdf/BUILD.gn
index f79f34511ae..1483ff4a9a0 100644
--- a/chromium/chrome/browser/resources/pdf/BUILD.gn
+++ b/chromium/chrome/browser/resources/pdf/BUILD.gn
@@ -10,6 +10,7 @@ group("closure_compile") {
":pdf_resources",
"elements/viewer-bookmark:closure_compile",
"elements/viewer-error-screen:closure_compile",
+ "elements/viewer-form-warning:closure_compile",
"elements/viewer-page-indicator:closure_compile",
"elements/viewer-page-selector:closure_compile",
"elements/viewer-password-screen:closure_compile",
@@ -53,13 +54,44 @@ js_library("pdf_scripting_api") {
js_library("viewport_scroller") {
}
+js_library("viewport_interface") {
+ deps = [
+ ":pdf_fitting_type",
+ ]
+}
+
+js_library("viewport") {
+ deps = [
+ ":gesture_detector",
+ ":viewport_interface",
+ ":zoom_manager",
+ "//ui/webui/resources/js:util",
+ ]
+ externs_list = [ "$externs_path/pending.js" ]
+}
+
+js_library("zoom_manager") {
+ deps = [
+ ":browser_api",
+ ":viewport_interface",
+ ]
+}
+
+js_library("metrics") {
+ externs_list = [ "$externs_path/metrics_private.js" ]
+}
+
js_type_check("pdf_resources") {
deps = [
":browser_api",
":gesture_detector",
+ ":metrics",
":open_pdf_params_parser",
":pdf_fitting_type",
":pdf_scripting_api",
+ ":viewport",
+ ":viewport_interface",
":viewport_scroller",
+ ":zoom_manager",
]
}
diff --git a/chromium/chrome/browser/resources/pdf/elements/icons.html b/chromium/chrome/browser/resources/pdf/elements/icons.html
index f71953e61f7..a5976b143ce 100644
--- a/chromium/chrome/browser/resources/pdf/elements/icons.html
+++ b/chromium/chrome/browser/resources/pdf/elements/icons.html
@@ -12,12 +12,14 @@
<g id="bookmark"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"></path></g>
<g id="bookmark-border"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"></path></g>
<g id="create"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path></g>
- <g id="eraser"><path d="m15.543 4.9863c-0.32193 0.019835-0.65088 0.1626-0.91016 0.42188l-4.8867 4.8867 5.5332 5.5332h-2.1211l-4.4727-4.4727-0.65234 0.65234c-0.51854 0.51855-0.56773 1.319-0.10938 1.7773l4.166 4.166c0.45836 0.45836 1.2588 0.40917 1.7773-0.10938l6.5996-6.5996c0.51854-0.51855 0.56773-1.319 0.10938-1.7773l-4.166-4.166c-0.22918-0.22918-0.54525-0.33233-0.86719-0.3125zm-12.543 13.764v1.5h0.75 6 0.75v-1.5h-0.75-6-0.75z"></path></g>
+ <g id="eraser"><path d="M21.41,11.33 L13.04,20 L4.73,20 L2.58,17.86 C1.8,17.08 1.8,15.83 2.58,15.04 L13.62,3.58 C14.4,2.81 15.68,2.81 16.46,3.58 L21.41,8.51 C22.2,9.29 22.2,10.55 21.41,11.33 L21.41,11.33 Z"></path><polygon points="17.26 18 15.26 20 21.96 20 21.96 18"></polygon></g>
<g id="fullscreen-exit"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"></path></g>
- <g id="highlighter"><path d="M15.7498169,15.9885 L18.69405,19.7475 C18.75405,19.8165 18.79455,19.89975 18.8148,19.98825 C17.22405,20.30025 14.37105,20.25 12.0648,20.25 C9.72705,20.25 6.61248962,20.295 5.01423962,19.9755 C5.03523962,19.8915 5.07348962,19.8135 5.13123962,19.7475 L8.25498962,15.9885 L8.25498962,11.3135317 C8.25498962,11.2722817 8.26398962,11.2317817 8.28123962,11.1957817 L8.59698962,10.5297817 C8.59698962,10.5 15.4078169,10.49925 15.4078169,10.5 L15.7235669,11.1957817 C15.7400669,11.2317817 15.7498169,11.2722817 15.7498169,11.3135317 L15.7498169,15.9885"></path><path d="M13.846962,4.03912354 L14.04,4.09664481 C14.163,4.13093052 14.25,4.25693052 14.25,4.40178767 L14.25,5.43243216 L14.25,5.7872893 L14.25,8.82657296 C13.6995,8.91057296 12.79575,9 12,9 C11.20275,9 10.3005,8.91057296 9.75,8.82571582 L9.75,4.41586073 L9.75,3.85871787 L9.75,3.3152893 C9.75,3.11043216 9.91725,2.96043216 10.09125,3.0092893 L13.846962,4.03912354 Z" style="fill: var(--pen-tip-fill)"></g>
- <g id="marker"><path d="M14.25,8.91853902 L14.25,6.53925711 C14.25,5.97869512 14.1161242,5.42520919 13.8582163,4.92039274 L12.7090497,2.67089612 C12.4225948,2.10981636 11.5774052,2.10964378 11.2909503,2.67072353 C10.911176,3.41422755 10.4575732,4.30184157 10.1413899,4.92108308 C9.88348208,5.42589953 9.75,5.97869512 9.75,6.53925711 L9.75,8.91940195 C10.4758827,8.97169576 11.2816971,9 11.9916328,9 C12.7058997,9 13.5195892,8.97152317 14.25,8.91853902" style="fill: var(--pen-tip-fill)"></path><path d="M15.7498169,15.9885 L15.7498169,11.3135317 C15.7498169,11.2722817 15.7400669,11.2317817 15.7235669,11.1957817 L15.4078169,10.5 C15.4078169,10.49925 8.59698962,10.5 8.59698962,10.5297817 L8.28123962,11.1957817 C8.26398962,11.2317817 8.25498962,11.2722817 8.25498962,11.3135317 L8.25498962,15.9885 L5.13123962,19.7475 C5.07348962,19.8135 5.03523962,19.8915 5.01423962,19.9755 C6.61248962,20.295 9.72705,20.25 12.0648,20.25 C14.37105,20.25 17.22405,20.30025 18.8148,19.98825 C18.79455,19.89975 18.75405,19.8165 18.69405,19.7475 L15.7498169,15.9885 Z"></path></g>
+ <g id="highlighter"><path d="M10.22,9.49 L4.31,15.49 C3.54,16.29 3.61,17.54 4.39,18.34 L0.77,22 L6.45,22 L7.19,21.25 C7.97,22.06 9.14,22.11 9.92,21.3 L15.88,15.25 L10.22,9.49 L10.22,9.49 Z"></path><path style="fill: var(--pen-tip-fill)" d="M22.68,5.49 L19.86,2.62 C19.08,1.82 17.79,1.78 17.02,2.58 L11.27,8.43 L16.93,14.18 L22.62,8.4 C23.39,7.59 23.45,6.29 22.68,5.49 L22.68,5.49 Z"></path><path style="fill: var(--pen-tip-border)" d="M18.4,3c0.3,0,0.5,0.1,0.7,0.3L22,6.2c0.4,0.4,0.4,1.1-0.1,1.5l-5,5.1l-4.3-4.3l5.1-5.2 C17.9,3.1,18.1,3,18.4,3 M18.4,2c-0.5,0-1,0.2-1.4,0.6l-5.8,5.9l5.7,5.8l5.7-5.8c0.8-0.8,0.8-2.1,0.1-2.9l-2.8-2.9 C19.5,2.2,18.9,2,18.4,2L18.4,2z"></path></g>
+ <g id="marker"><polygon points="3 17.25 3 21 6.74 21 14.28 13.47 10.53 9.72"></polygon><path style="fill: var(--pen-tip-fill)" d="M18.37,3.3 L20.71,5.63 C21.1,6.02 21.11,6.66 20.72,7.05 L15.35,12.41 L11.59,8.65 L14.12,6.12 L13.39,5.39 L7.73,11.05 L6.33,9.65 L12.7,3.29 C13.09,2.9 13.74,2.91 14.12,3.3 L15.54,4.71 L16.96,3.3 C17.34,2.91 17.98,2.91 18.37,3.3 L18.37,3.3 Z"></path><path style="fill: var(--pen-tip-border)" d="M17.7,4L20,6.3L15.4,11L13,8.6l1.8-1.8l0.7-0.7l-0.7-0.7l-0.2-0.2l0.2,0.2l0.7,0.7l0.7-0.7L17.7,4 M13.4,3 c-0.3,0-0.5,0.1-0.7,0.3L6.3,9.6l1.4,1.4l5.7-5.7l0.7,0.7l-2.5,2.5l3.8,3.8L20.7,7c0.4-0.4,0.4-1,0-1.4l-2.3-2.3 C18.2,3.1,17.9,3,17.7,3S17.2,3.1,17,3.3l-1.4,1.4l-1.4-1.4C13.9,3.1,13.7,3,13.4,3L13.4,3z"></path></g>
+ <g id="redo"><path d="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z"></path></g>
<g id="remove"><path d="M19 13H5v-2h14v2z"></path></g>
<g id="rotate-right"><path d="M15.55 5.55L11 1v3.07C7.06 4.56 4 7.92 4 12s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91V10l4.55-4.45zM19.93 11c-.17-1.39-.72-2.73-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zM13 17.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"></path></g>
+ <g id="undo"><path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"></path></g>
</defs>
</svg>
</iron-iconset-svg>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn
new file mode 100644
index 00000000000..e27ead67627
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2019 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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+ deps = [
+ ":viewer-form-warning",
+ ]
+}
+
+js_library("viewer-form-warning") {
+ deps = [
+ "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
+ "//ui/webui/resources/js:promise_resolver",
+ ]
+}
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html
new file mode 100644
index 00000000000..e86b7915e37
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html
@@ -0,0 +1,25 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+
+<dom-module id="viewer-form-warning">
+ <template>
+ <style include="paper-button-style cr-hidden-style"></style>
+ <cr-dialog id="dialog" no-cancel>
+ <div slot="title">[[strings.annotationFormWarningTitle]]</div>
+ <div slot="body">[[strings.annotationFormWarningDetail]]</div>
+ <div slot="button-container">
+ <paper-button class="cancel-button" on-click="onCancel">
+ [[strings.annotationFormWarningKeepEditing]]
+ </paper-button>
+ <paper-button class="action-button" on-click="onAction">
+ [[strings.annotationFormWarningDiscard]]
+ </paper-button>
+ </div>
+ </cr-dialog>
+ </template>
+ <script src="viewer-form-warning.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js
new file mode 100644
index 00000000000..539eddd6b67
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js
@@ -0,0 +1,29 @@
+// Copyright 2019 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({
+ is: 'viewer-form-warning',
+ properties: {
+ strings: Object,
+ },
+
+ /** @private {PromiseResolver} */
+ resolver_: null,
+
+ show: function() {
+ this.resolver_ = new PromiseResolver();
+ /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+ return this.resolver_.promise;
+ },
+
+ onCancel: function() {
+ this.resolver_.reject();
+ this.$.dialog.cancel();
+ },
+
+ onAction: function() {
+ this.resolver_.resolve();
+ this.$.dialog.close();
+ },
+});
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn
index 420a227722d..32ca2b04311 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn
@@ -12,11 +12,8 @@ js_type_check("closure_compile") {
js_library("viewer-ink-host") {
deps = [
+ "//chrome/browser/resources/pdf:metrics",
+ "//chrome/browser/resources/pdf:viewport",
"//chrome/browser/resources/pdf/ink:ink_api",
]
- externs_list = [
- # TODO(dstockwell): Once viewport can be typechecked this can be replaced
- # by a dep on viewport.
- "externs.js",
- ]
}
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js
deleted file mode 100644
index f3769e25a2a..00000000000
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 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.
-
-/**
- * @typedef {{
- * x: number,
- * y: number
- * }}
- */
-let Point;
-
-/**
- * @typedef {{
- * width: number,
- * height: number
- * }}
- */
-let Size;
-
-class Viewport {
- /**
- * @param {number} zoom
- * @return {{width: number, height: number}}
- */
- getDocumentDimensions(zoom) {}
-
- /**
- * @param {!Point} point
- * @return {boolean}
- */
- isPointInsidePage(point) {}
-
- /** @return {!Point} */
- get position() {}
-
- /** @return {!Size} */
- get size() {}
-
- /** @return {number} */
- get zoom() {}
-}
-
-/** @type {Object} */
-Viewport.PAGE_SHADOW;
-
-/** @type {number} */
-Viewport.PAGE_SHADOW.top;
-
-/** @type {number} */
-Viewport.PAGE_SHADOW.left;
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html
index 120d6ecc60e..1a7a2ff73ea 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html
@@ -4,7 +4,6 @@
<template>
<style>
:host {
- touch-action: none;
visibility: hidden;
}
iframe {
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
index 1895a2347d5..6bb2e4c68ed 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
@@ -50,6 +50,9 @@ Polymer({
/** @type {Viewport} */
viewport: null,
+ /** @type {?AnnotationTool} */
+ tool_: null,
+
/**
* Whether we should suppress pointer events due to a gesture,
* eg. pinch-zoom.
@@ -168,6 +171,26 @@ Polymer({
this.activePointer_ = null;
if (!this.pointerGesture_) {
this.dispatchPointerEvent_(e);
+ // If the stroke was not cancelled (type == pointercanel),
+ // notify about mutation and record metrics.
+ if (e.type == 'pointerup') {
+ this.dispatchEvent(new CustomEvent('stroke-added'));
+ if (e.pointerType == 'mouse') {
+ PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_MOUSE);
+ } else if (e.pointerType == 'pen') {
+ PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_PEN);
+ } else if (e.pointerType == 'touch') {
+ PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_TOUCH);
+ }
+ if (this.tool_.tool == 'eraser') {
+ PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_ERASER);
+ } else if (this.tool_.tool == 'pen') {
+ PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_PEN);
+ } else if (this.tool_.tool == 'highlighter') {
+ PDFMetrics.record(
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_HIGHLIGHTER);
+ }
+ }
}
this.pointerGesture_ = false;
},
@@ -202,12 +225,15 @@ Polymer({
this.$.frame.src = 'ink/index.html';
await new Promise(resolve => this.$.frame.onload = resolve);
this.ink_ = await this.$.frame.contentWindow.initInk();
+ this.ink_.addUndoStateListener(
+ e => this.dispatchEvent(
+ new CustomEvent('undo-state-changed', {detail: e})));
this.ink_.setPDF(data);
this.state_ = State.ACTIVE;
this.viewportChanged();
- // TODO(dstockwell): we shouldn't need this extra flush.
- await this.ink_.flush();
- await this.ink_.flush();
+ // Wait for the next task to avoid a race where Ink drops the background
+ // color.
+ await new Promise(resolve => setTimeout(resolve));
this.ink_.setOutOfBoundsColor(BACKGROUND_COLOR);
const spacing = Viewport.PAGE_SHADOW.top + Viewport.PAGE_SHADOW.bottom;
this.ink_.setPageSpacing(spacing);
@@ -222,7 +248,7 @@ Polymer({
const pos = viewport.position;
const size = viewport.size;
const zoom = viewport.zoom;
- const documentWidth = viewport.getDocumentDimensions(zoom).width * zoom;
+ const documentWidth = viewport.getDocumentDimensions().width * zoom;
// Adjust for page shadows.
const y = pos.y - Viewport.PAGE_SHADOW.top * zoom;
let x = pos.x - Viewport.PAGE_SHADOW.left * zoom;
@@ -248,6 +274,16 @@ Polymer({
this.ink_.setCamera(camera);
},
+ /** Undo the last edit action. */
+ undo() {
+ this.ink_.undo();
+ },
+
+ /** Redo the last undone edit action. */
+ redo() {
+ this.ink_.redo();
+ },
+
/**
* @return {!Promise<{fileName: string, dataToSave: ArrayBuffer}>}
* The serialized PDF document including any annotations that were made.
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
index 713abee0bfe..921ce0f27a3 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
@@ -1,4 +1,6 @@
<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
@@ -12,7 +14,7 @@
<dom-module id="viewer-pdf-toolbar">
<template>
- <style>
+ <style include="cr-hidden-style">
:host ::selection {
background: rgba(255, 255, 255, 0.3);
}
@@ -50,11 +52,20 @@
}
paper-icon-button {
- margin-inline-end: 12px;
+ height: 36px;
+ margin: 6px;
+ padding: 8px;
+ width: 36px;
}
- viewer-toolbar-dropdown {
- margin-inline-end: 4px;
+ paper-icon-button:hover {
+ background: rgba(255, 255, 255, 0.08);
+ border-radius: 50%;
+ }
+
+ paper-icon-button:focus {
+ --paper-icon-button-ink-color:white;
+ --paper-ripple-opacity: 0.24;
}
paper-progress {
@@ -100,25 +111,45 @@
--dropdown-width: 346px;
}
- #eraser:not([selected]),
- #pen:not([selected]),
- #highlighter:not([selected]) {
- filter: contrast(30%);
- }
-
#pen,
#highlighter {
--dropdown-open-background: rgb(50, 54, 57);
}
- #eraser[selected] {
- background-color: rgb(50, 54, 57);
- border-radius: 4px;
+ #eraser {
+ opacity: 0.38;
+ }
+
+ #eraser[selected],
+ #eraser:focus,
+ #eraser:hover {
+ opacity: 1;
+ }
+
+ #annotation-separator {
+ background: white;
+ height: 30px;
+ margin-inline-end: 12px;
+ margin-inline-start: 12px;
+ opacity: 0.38;
+ width: 1px;
}
:host([annotation-mode]) #annotate {
- background-color: rgb(25, 27, 29);
- border-radius: 4px;
+ background-color: rgba(255, 255, 255, 0.24);
+ border-radius: 50%;
+ }
+
+ #bookmarks {
+ margin-inline-start: 8px;
+ }
+
+ #pen {
+ margin-inline-end: 10px;
+ }
+
+ #highlighter {
+ margin-inline-end: 6px;
}
.invisible {
@@ -165,6 +196,7 @@
<div id="buttons" class="invisible">
<template is="dom-if" if="[[pdfAnnotationsEnabled]]">
<paper-icon-button id="annotate" icon="pdf:create"
+ disabled="[[!annotationAvailable]]"
on-click="toggleAnnotation"
aria-label$="{{strings.tooltipAnnotate}}"
title$="{{strings.tooltipAnnotate}}">
@@ -172,6 +204,7 @@
</template>
<paper-icon-button id="rotate-right" icon="pdf:rotate-right"
+ disabled="[[annotationMode]]"
on-click="rotateRight"
aria-label$="{{strings.tooltipRotateCW}}"
title$="{{strings.tooltipRotateCW}}">
@@ -190,6 +223,7 @@
</paper-icon-button>
<viewer-toolbar-dropdown id="bookmarks"
+ selected
metrics-id="bookmarks"
hidden$="[[!bookmarks.length]]"
open-icon="pdf:bookmark"
@@ -201,19 +235,13 @@
</div>
</div>
<div id="progress-container">
- <paper-progress id="progress" value="[[loadProgress]]"></paper-progress>
+ <paper-progress id="progress"
+ value="[[loadProgress]]"
+ indeterminate="[[annotationMode]]"></paper-progress>
</div>
</div>
- <div id="annotations-bar" class="invisible">
- <paper-icon-button id="eraser"
- selected$="[[equal_('eraser', annotationTool.tool)]]"
- on-click="annotationToolClicked_"
- icon="pdf:eraser"
- aria-label$="{{strings.annotationEraser}}"
- title$="{{strings.annotationEraser}}">
- </paper-icon-button>
-
+ <div id="annotations-bar" hidden>
<viewer-toolbar-dropdown id="pen"
selected$="[[equal_('pen', annotationTool.tool)]]"
open-after-select
@@ -251,6 +279,32 @@
on-selected-color-changed="annotationToolOptionChanged_">
</viewer-pen-options>
</viewer-toolbar-dropdown>
+
+ <paper-icon-button id="eraser"
+ selected$="[[equal_('eraser', annotationTool.tool)]]"
+ on-click="annotationToolClicked_"
+ icon="pdf:eraser"
+ aria-label$="{{strings.annotationEraser}}"
+ title$="{{strings.annotationEraser}}">
+ </paper-icon-button>
+
+ <div id="annotation-separator"></div>
+
+ <paper-icon-button id="undo"
+ disabled="[[!canUndoAnnotation]]"
+ icon="pdf:undo"
+ on-click="undo"
+ aria-label$="{{strings.annotationUndo}}"
+ title$="{{strings.annotationUndo}}">
+ </paper-icon-button>
+
+ <paper-icon-button id="redo"
+ disabled="[[!canRedoAnnotation]]"
+ icon="pdf:redo"
+ on-click="redo"
+ aria-label$="{{strings.annotationRedo}}"
+ title$="{{strings.annotationRedo}}">
+ </paper-icon-button>
</div>
</template>
<script src="viewer-pdf-toolbar.js"></script>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js
index 5c61b3b8e98..8e18564aa75 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js
@@ -53,9 +53,32 @@ Polymer({
},
/**
+ * Whether annotation mode can be entered. This would be false if for
+ * example the PDF is encrypted or password protected. Note, this is
+ * true regardless of whether the feature flag is enabled.
+ */
+ annotationAvailable: {
+ type: Boolean,
+ value: true,
+ },
+
+ canUndoAnnotation: {
+ type: Boolean,
+ value: false,
+ },
+
+ canRedoAnnotation: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
* Whether the PDF Annotations feature is enabled.
*/
- pdfAnnotationsEnabled: Boolean,
+ pdfAnnotationsEnabled: {
+ type: Boolean,
+ value: false,
+ },
strings: Object,
},
@@ -72,8 +95,7 @@ Polymer({
this.$.pageselector.classList.toggle('invisible', !loaded);
this.$.buttons.classList.toggle('invisible', !loaded);
this.$.progress.style.opacity = loaded ? 0 : 1;
- this.$['annotations-bar'].classList.toggle(
- 'invisible', !(loaded && this.annotationMode));
+ this.$['annotations-bar'].hidden = !loaded || !this.annotationMode;
}
},
@@ -163,12 +185,25 @@ Polymer({
this.fire('print');
},
+ undo: function() {
+ this.fire('undo');
+ },
+
+ redo: function() {
+ this.fire('redo');
+ },
+
toggleAnnotation: function() {
this.annotationMode = !this.annotationMode;
if (this.annotationMode) {
// Select pen tool when entering annotation mode.
this.updateAnnotationTool_(this.$.pen);
}
+ this.dispatchEvent(new CustomEvent('annotation-mode-toggled', {
+ detail: {
+ value: this.annotationMode,
+ },
+ }));
},
/** @param {Event} e */
@@ -193,6 +228,10 @@ Polymer({
selectedColor: null,
};
element.attributeStyleMap.set('--pen-tip-fill', options.selectedColor);
+ element.attributeStyleMap.set(
+ '--pen-tip-border',
+ options.selectedColor == '#000000' ? 'currentcolor' :
+ options.selectedColor);
this.annotationTool = {
tool: tool,
size: options.selectedSize,
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
index e582fdf4ba7..3151736c3e3 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
@@ -67,13 +67,14 @@
checked$="[[equal_(selectedColor, item.color)]]"
tabindex="1" style="--item-color: [[item.color]]"
title$="[[lookup_(strings, item.name)]]"
- aria-label$="[[lookup_(strings, item.name)]]">
+ aria-label$="[[lookup_(strings, item.name)]]"
+ on-pointerdown="blurOnPointerDown">
</template>
<paper-icon-button id="expand" icon="cr:expand-more"
tabindex="3"
on-click="toggleExpanded_"
- aria-label$="[[strings.tooltipExpand]]"
- title$="[[strings.tooltipExpand]]">
+ aria-label$="[[strings.annotationExpand]]"
+ title$="[[strings.annotationExpand]]">
</paper-icon-button>
</div>
<div id="separator"></div>
@@ -83,10 +84,11 @@
checked$="[[equal_(selectedSize, item.size)]]"
tabindex="2" style="--item-size: [[item.size]]"
title$="{{lookup_(strings, item.name)}}"
- aria-label$="[[lookup_(strings, item.name)]]">
+ aria-label$="[[lookup_(strings, item.name)]]"
+ on-pointerdown="blurOnPointerDown">
</template>
</div>
</paper-icon-button>
</template>
<script src="viewer-pen-options.js"></script>
-</dom-module> \ No newline at end of file
+</dom-module>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js
index e01a388627a..e357009cb2b 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js
@@ -94,18 +94,33 @@ Polymer({
this.selectedColor = e.target.value;
},
+ /** @private */
toggleExpanded_: function() {
this.expanded_ = !this.expanded_;
this.updateExpandedState_();
},
- attached() {
+ /** @private */
+ updateExpandedStateAndFinishAnimations_: function() {
this.updateExpandedState_();
for (const animation of this.expandAnimations_) {
animation.finish();
}
},
+ /** @override */
+ attached: function() {
+ // TODO (rbpotter): Remove this conditional when the migration to Polymer 2
+ // is completed.
+ if (Polymer.DomIf) {
+ Polymer.RenderStatus.beforeNextRender(this, () => {
+ this.updateExpandedStateAndFinishAnimations_();
+ });
+ } else {
+ this.updateExpandedStateAndFinishAnimations_();
+ }
+ },
+
/**
* Updates the state of the UI to reflect the current value of `expanded`.
* Starts or reverses animations and enables/disable controls.
@@ -133,14 +148,16 @@ Polymer({
}),
];
}
- if (this.expanded_) {
- for (const animation of this.expandAnimations_) {
- animation.playbackRate = 1;
- }
- } else {
- for (const animation of this.expandAnimations_) {
- animation.playbackRate = -1;
- }
+ for (const animation of this.expandAnimations_) {
+ // TODO(dstockwell): Ideally we would just set playbackRate,
+ // but there appears to be a web-animations bug that
+ // results in the animation getting stuck in the 'pending'
+ // state sometimes. See crbug.com/938857
+ const currentTime = animation.currentTime;
+ animation.cancel();
+ animation.playbackRate = this.expanded_ ? 1 : -1;
+ animation.currentTime = currentTime;
+ animation.play();
}
for (const input of colors.querySelectorAll('input:nth-child(n+8)')) {
if (this.expanded_) {
@@ -157,7 +174,7 @@ Polymer({
* @param {*} a
* @param {*} b
*/
- equal_: function(a,b) {
+ equal_: function(a, b) {
return a == b;
},
@@ -169,6 +186,16 @@ Polymer({
* @return {string}
*/
lookup_: function(strings, name) {
- return strings[name];
- }
-}); \ No newline at end of file
+ return strings ? strings[name] : '';
+ },
+
+ /**
+ * Used to remove focus when clicking or tapping on a styled input
+ * element. This is a workaround until we can use the :focus-visible
+ * pseudo selector.
+ */
+ blurOnPointerDown(e) {
+ const target = e.target;
+ setTimeout(() => target.blur(), 0);
+ },
+});
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html
index 9401afc44ef..50dadbe03bd 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html
@@ -1,6 +1,6 @@
<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
@@ -8,11 +8,16 @@
<template>
<style>
:host {
+ display: inline-block;
position: relative;
text-align: start;
--dropdown-width: 260px;
}
+ :host-context([hidden]) {
+ display: none;
+ }
+
:host([dropdown-centered]) {
--container-offset: calc(50% - var(--dropdown-width) / 2);
}
@@ -43,20 +48,37 @@
padding: 6px 0 4px 0;
}
- #icon {
+ #button {
+ border-radius: 4px;
cursor: pointer;
display: inline-block;
+ height: 32px;
+ margin: 0;
+ min-width: 48px;
+ opacity: 0.38;
+ padding-bottom: 6px;
+ padding-inline-end: 2px;
+ padding-inline-start: 6px;
+ padding-top: 6px;
+ width: 48px;
}
- :host([dropdown-open]) #icon,
- :host([selected]) #icon {
- background-color: var(--dropdown-open-background, rgb(25, 27, 29));
- border-radius: 4px;
+ #button:focus {
+ background-color: rgba(255, 255, 255, 0.24);
+ opacity: 1;
}
- #arrow {
- margin-inline-start: -12px;
- padding-inline-end: 4px;
+ #button:hover {
+ background-color: rgba(255, 255, 255, 0.08);
+ opacity: 1;
+ }
+
+ :host([selected]) #button {
+ opacity: 1;
+
+ }
+ :host([dropdown-open]) #button {
+ background-color: rgba(255, 255, 255, 0.24);
}
h1 {
@@ -67,12 +89,11 @@
padding: 14px 28px;
}
</style>
- <div on-click="toggleDropdown" id="icon">
- <paper-icon-button id="main-icon" icon="[[dropdownIcon]]"
+ <paper-button on-click="toggleDropdown" id="button"
aria-label$="{{header}}" title$="{{header}}">
- </paper-icon-button>
- <iron-icon icon="cr:arrow-drop-down" id="arrow"></iron-icon>
- </div>
+ <iron-icon icon="[[dropdownIcon]]"></iron-icon>
+ <iron-icon icon="cr:arrow-drop-down"></iron-icon>
+ </paper-button>
<div id="container">
<div id="dropdown" style="display: none">
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js
index 0ce8ebd6667..05f044afe47 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js
@@ -101,6 +101,7 @@ Polymer({
}
if (this.dropdownOpen) {
this.toggleDropdown();
+ this.blur();
}
// Clean up the handler. The dropdown may already be closed.
window.removeEventListener('pointerdown', listener);
diff --git a/chromium/chrome/browser/resources/pdf/index.html b/chromium/chrome/browser/resources/pdf/index.html
index 3450bd52e66..71b5d84126d 100644
--- a/chromium/chrome/browser/resources/pdf/index.html
+++ b/chromium/chrome/browser/resources/pdf/index.html
@@ -9,9 +9,11 @@
<link rel="import" href="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html">
<link rel="import" href="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html">
<link rel="import" href="elements/shared-vars.html">
+ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<if expr="chromeos">
<link rel="import" href="elements/viewer-ink-host/viewer-ink-host.html">
+ <link rel="import" href="elements/viewer-form-warning/viewer-form-warning.html">
</if>
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
@@ -30,11 +32,16 @@
<viewer-error-screen id="error-screen"></viewer-error-screen>
+<if expr="chromeos">
+<viewer-form-warning id="form-warning"></viewer-form-warning>
+</if>
+
<div id="content"></div>
</body>
<script src="pdf_fitting_type.js"></script>
<script src="toolbar_manager.js"></script>
+<script src="viewport_interface.js"></script>
<script src="viewport.js"></script>
<script src="open_pdf_params_parser.js"></script>
<script src="navigator.js"></script>
diff --git a/chromium/chrome/browser/resources/pdf/ink/BUILD.gn b/chromium/chrome/browser/resources/pdf/ink/BUILD.gn
index ff59d37b78c..b98fd60781e 100644
--- a/chromium/chrome/browser/resources/pdf/ink/BUILD.gn
+++ b/chromium/chrome/browser/resources/pdf/ink/BUILD.gn
@@ -11,10 +11,7 @@ group("closure_compile") {
}
js_library("ink_api") {
- externs_list = [
- "externs.js",
- "../../../../../third_party/ink/build/ink_lib_externs.js",
- ]
+ externs_list = [ "//third_party/ink/build/ink_lib_externs.js" ]
}
js_type_check("ink") {
diff --git a/chromium/chrome/browser/resources/pdf/ink/ink_api.js b/chromium/chrome/browser/resources/pdf/ink/ink_api.js
index bec99a72ec1..04e0d56dcf6 100644
--- a/chromium/chrome/browser/resources/pdf/ink/ink_api.js
+++ b/chromium/chrome/browser/resources/pdf/ink/ink_api.js
@@ -12,6 +12,14 @@
let AnnotationTool;
/**
+ * @typedef {{
+ * canUndo: boolean,
+ * canRedo: boolean,
+ * }}
+ */
+let UndoState;
+
+/**
* Wraps the Ink component with an API that can be called
* across an IFrame boundary.
*/
@@ -20,6 +28,20 @@ class InkAPI {
constructor(embed) {
this.embed_ = embed;
this.brush_ = ink.BrushModel.getInstance(embed);
+ this.camera_ = null;
+ }
+
+ /** @param {function(!UndoState)} listener */
+ addUndoStateListener(listener) {
+ /** @param {!ink.UndoStateChangeEvent} e */
+ function wrapper(e) {
+ listener({
+ canUndo: e.getCanUndo(),
+ canRedo: e.getCanRedo(),
+ });
+ }
+
+ this.embed_.addEventListener(ink.UndoStateChangeEvent.EVENT_TYPE, wrapper);
}
/**
@@ -49,8 +71,12 @@ class InkAPI {
return this.embed_.getPDFDestructive();
}
- setCamera(camera) {
+ async setCamera(camera) {
+ this.camera_ = camera;
this.embed_.setCamera(camera);
+ // Wait for the next task to avoid a race where Ink drops the camera value
+ // when the canvas is rotated in low-latency mode.
+ setTimeout(() => this.embed_.setCamera(this.camera_), 0);
}
/** @param {AnnotationTool} tool */
@@ -58,7 +84,7 @@ class InkAPI {
const shape = {
eraser: 'MAGIC_ERASE',
pen: 'INKPEN',
- highlighter: 'HIGHLIGHTER',
+ highlighter: 'SMART_HIGHLIGHTER_TOOL',
}[tool.tool];
this.brush_.setShape(shape);
if (tool.tool != 'eraser') {
@@ -87,8 +113,40 @@ class InkAPI {
}
dispatchPointerEvent(type, init) {
+ const engine = document.querySelector('#ink-engine');
+ const match = engine.style.transform.match(/(\d+)deg/);
+ const rotation = match ? Number(match[1]) : 0;
+ let offsetX = init.clientX;
+ let offsetY = init.clientY;
+ // If Ink's canvas has been re-orientated away from 0, we must transform
+ // the event's offsetX and offsetY to correspond with the rotation and
+ // offset applied.
+ if ([90, 180, 270].includes(rotation)) {
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+ const matrix = new DOMMatrix();
+ matrix.translateSelf(width / 2, height / 2);
+ matrix.rotateSelf(0, 0, -rotation);
+ matrix.translateSelf(-width / 2, -height / 2);
+ const result = matrix.transformPoint({x: offsetX, y: offsetY});
+ offsetX = result.x - engine.offsetLeft;
+ offsetY = result.y - engine.offsetTop;
+ }
+
const event = new PointerEvent(type, init);
- document.querySelector('#ink-engine').dispatchEvent(event);
+ // Ink uses offsetX and offsetY, but we can only override them, not pass
+ // as part of the init.
+ Object.defineProperty(event, 'offsetX', {value: offsetX});
+ Object.defineProperty(event, 'offsetY', {value: offsetY});
+ engine.dispatchEvent(event);
+ }
+
+ undo() {
+ this.embed_.undo();
+ }
+
+ redo() {
+ this.embed_.redo();
}
}
diff --git a/chromium/chrome/browser/resources/pdf/metrics.js b/chromium/chrome/browser/resources/pdf/metrics.js
index d0cb77c8d97..d55c0d1f731 100644
--- a/chromium/chrome/browser/resources/pdf/metrics.js
+++ b/chromium/chrome/browser/resources/pdf/metrics.js
@@ -2,211 +2,225 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-(function() {
-
-'use strict';
-
-// Keep in sync with enums.xml.
-// Do not change the numeric values or reuse them since these numbers are
-// persisted to logs.
-const UserAction = {
- DOCUMENT_OPENED: 0, // Baseline to use as denominator for all formulas.
- ROTATE_FIRST: 1,
- ROTATE: 2,
- FIT_TO_WIDTH_FIRST: 3,
- FIT_TO_WIDTH: 4,
- FIT_TO_PAGE_FIRST: 5,
- FIT_TO_PAGE: 6,
- OPEN_BOOKMARKS_PANEL_FIRST: 7,
- OPEN_BOOKMARKS_PANEL: 8,
- FOLLOW_BOOKMARK_FIRST: 9,
- FOLLOW_BOOKMARK: 10,
- PAGE_SELECTOR_NAVIGATE_FIRST: 11,
- PAGE_SELECTOR_NAVIGATE: 12,
- NUMBER_OF_ACTIONS: 13
-};
/**
* Handles events specific to the PDF viewer and logs the corresponding metrics.
- *
- * @interface
*/
-window.PDFMetrics = class {
- constructor() {}
-
- /**
- * Call when the document is first loaded. This event serves as denominator to
- * determine percentages of documents in which an action was taken as well as
- * average number of each action per document.
- */
- onDocumentOpened() {}
-
- /**
- * Call when the document is rotated clockwise or counter-clockwise.
- */
- onRotation() {}
-
+class PDFMetrics {
/**
- * Call when the zoom mode is changed to fit a FittingType.
+ * Records when the zoom mode is changed to fit a FittingType.
*
* @param {FittingType} fittingType the new FittingType.
*/
- onFitTo(fittingType) {}
+ static recordFitTo(fittingType) {
+ if (fittingType == FittingType.FIT_TO_PAGE) {
+ PDFMetrics.record(PDFMetrics.UserAction.FIT_TO_PAGE);
+ } else if (fittingType == FittingType.FIT_TO_WIDTH) {
+ PDFMetrics.record(PDFMetrics.UserAction.FIT_TO_WIDTH);
+ }
+ // There is no user action to do a fit-to-height, this only happens with
+ // the open param "view=FitV".
+ }
/**
- * Call when the bookmarks panel is opened.
+ * Records the given action to chrome.metricsPrivate.
+ *
+ * @param {PDFMetrics.UserAction} action
*/
- onOpenBookmarksPanel() {}
+ static record(action) {
+ if (!chrome.metricsPrivate) {
+ return;
+ }
+ if (!PDFMetrics.actionsMetric_) {
+ PDFMetrics.actionsMetric_ = {
+ 'metricName': 'PDF.Actions',
+ 'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG,
+ 'min': 1,
+ 'max': PDFMetrics.UserAction.NUMBER_OF_ACTIONS,
+ 'buckets': PDFMetrics.UserAction.NUMBER_OF_ACTIONS + 1
+ };
+ }
+ chrome.metricsPrivate.recordValue(PDFMetrics.actionsMetric_, action);
+ if (PDFMetrics.firstMap_.has(action)) {
+ const firstAction = PDFMetrics.firstMap_.get(action);
+ if (!PDFMetrics.firstActionRecorded_.has(firstAction)) {
+ chrome.metricsPrivate.recordValue(
+ PDFMetrics.actionsMetric_, firstAction);
+ PDFMetrics.firstActionRecorded_.add(firstAction);
+ }
+ }
+ }
- /**
- * Call when a bookmark is followed.
- */
- onFollowBookmark() {}
+ static resetForTesting() {
+ PDFMetrics.firstActionRecorded_.clear();
+ PDFMetrics.actionsMetric_ = null;
+ }
+}
- /**
- * Call when the page selection is used to navigate to another page.
- */
- onPageSelectorNavigation() {}
-};
+/** @private {?chrome.metricsPrivate.MetricType} */
+PDFMetrics.actionsMetric_ = null;
+
+/** @private {Set} */
+PDFMetrics.firstActionRecorded_ = new Set();
+// Keep in sync with enums.xml.
+// Do not change the numeric values or reuse them since these numbers are
+// persisted to logs.
/**
- * Dummy implementation of PDFMetrics.
- * This is used in print preview mode to avoid bundling the actions in the PDF
- * viewer and the print preview in the same histogram. Also, metricsPrivate is
- * not available in print preview.
+ * User Actions that can be recorded by calling PDFMetrics.record.
+ * The *_FIRST values are recorded automaticlly,
+ * eg. PDFMetrics.record(...ROTATE) will also record ROTATE_FIRST
+ * on the first instance.
*
- * @implements {PDFMetrics}
+ * @enum {number}
*/
-window.PDFMetricsDummy = class {
- constructor() {}
+PDFMetrics.UserAction = {
+ /**
+ * Recorded when the document is first loaded. This event serves as
+ * denominator to determine percentages of documents in which an action was
+ * taken as well as average number of each action per document.
+ */
+ DOCUMENT_OPENED: 0,
- /** @override */
- onDocumentOpened() {}
+ /** Recorded when the document is rotated clockwise or counter-clockwise. */
+ ROTATE_FIRST: 1,
+ ROTATE: 2,
- /** @override */
- onRotation() {}
+ FIT_TO_WIDTH_FIRST: 3,
+ FIT_TO_WIDTH: 4,
- /** @override */
- onFitTo(fittingType) {}
+ FIT_TO_PAGE_FIRST: 5,
+ FIT_TO_PAGE: 6,
- /** @override */
- onOpenBookmarksPanel() {}
+ /** Recorded when the bookmarks panel is opened. */
+ OPEN_BOOKMARKS_PANEL_FIRST: 7,
+ OPEN_BOOKMARKS_PANEL: 8,
- /** @override */
- onFollowBookmark() {}
+ /** Recorded when a bookmark is followed. */
+ FOLLOW_BOOKMARK_FIRST: 9,
+ FOLLOW_BOOKMARK: 10,
- /** @override */
- onPageSelectorNavigation() {}
-};
+ /** Recorded when the page selection is used to navigate to another page. */
+ PAGE_SELECTOR_NAVIGATE_FIRST: 11,
+ PAGE_SELECTOR_NAVIGATE: 12,
-/**
- * Implementation of PDFMetrics that logs the corresponding metrics to UMA
- * through chrome.metricsPrivate.
- *
- * @implements {PDFMetrics}
- */
-window.PDFMetricsImpl = class {
- constructor() {
- /**
- * @private {Set}
- */
- this.firstEventLogged_ = new Set();
-
- /**
- * @private {Object}
- */
- this.actionsMetric_ = {
- 'metricName': 'PDF.Actions',
- 'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG,
- 'min': 1,
- 'max': UserAction.NUMBER_OF_ACTIONS,
- 'buckets': UserAction.NUMBER_OF_ACTIONS + 1
- };
- }
+ /** Recorded when the user triggers a save of the document. */
+ SAVE_FIRST: 13,
+ SAVE: 14,
- /** @override */
- onDocumentOpened() {
- this.logOnlyFirstTime_(UserAction.DOCUMENT_OPENED);
- }
+ /**
+ * Recorded when the user triggers a save of the document and the document
+ * has been modified by annotations.
+ */
+ SAVE_WITH_ANNOTATION_FIRST: 15,
+ SAVE_WITH_ANNOTATION: 16,
- /** @override */
- onRotation() {
- this.logFirstAndTotal_(UserAction.ROTATE_FIRST, UserAction.ROTATE);
- }
+ PRINT_FIRST: 17,
+ PRINT: 18,
- /** @override */
- onFitTo(fittingType) {
- if (fittingType == FittingType.FIT_TO_PAGE) {
- this.logFirstAndTotal_(
- UserAction.FIT_TO_PAGE_FIRST, UserAction.FIT_TO_PAGE);
- } else if (fittingType == FittingType.FIT_TO_WIDTH) {
- this.logFirstAndTotal_(
- UserAction.FIT_TO_WIDTH_FIRST, UserAction.FIT_TO_WIDTH);
- }
- // There is no user action to do a fit-to-height, this only happens with
- // the open param "view=FitV".
- }
+ ENTER_ANNOTATION_MODE_FIRST: 19,
+ ENTER_ANNOTATION_MODE: 20,
- /** @override */
- onOpenBookmarksPanel() {
- this.logFirstAndTotal_(
- UserAction.OPEN_BOOKMARKS_PANEL_FIRST, UserAction.OPEN_BOOKMARKS_PANEL);
- }
+ EXIT_ANNOTATION_MODE_FIRST: 21,
+ EXIT_ANNOTATION_MODE: 22,
- /** @override */
- onFollowBookmark() {
- this.logFirstAndTotal_(
- UserAction.FOLLOW_BOOKMARK_FIRST, UserAction.FOLLOW_BOOKMARK);
- }
+ /** Recorded when a pen stroke is made. */
+ ANNOTATE_STROKE_TOOL_PEN_FIRST: 23,
+ ANNOTATE_STROKE_TOOL_PEN: 24,
- /** @override */
- onPageSelectorNavigation() {
- this.logFirstAndTotal_(
- UserAction.PAGE_SELECTOR_NAVIGATE_FIRST,
- UserAction.PAGE_SELECTOR_NAVIGATE);
- }
+ /** Recorded when an eraser stroke is made. */
+ ANNOTATE_STROKE_TOOL_ERASER_FIRST: 25,
+ ANNOTATE_STROKE_TOOL_ERASER: 26,
- /**
- * Logs the "first" event code if it hasn't been logged by this instance yet
- * and also log the "total" event code. This distinction allows analyzing
- * both:
- * - in what percentage of documents each action was taken;
- * - how many times, on average, each action is taken on a document;
- *
- * @param {number} firstEventCode event code for the "first" metric.
- * @return {number} totalEventCode event code for the "total" metric.
- * @private
- */
- logFirstAndTotal_(firstEventCode, totalEventCode) {
- this.log_(totalEventCode);
- this.logOnlyFirstTime_(firstEventCode);
- }
+ /** Recorded when a highlighter stroke is made. */
+ ANNOTATE_STROKE_TOOL_HIGHLIGHTER_FIRST: 27,
+ ANNOTATE_STROKE_TOOL_HIGHLIGHTER: 28,
- /**
- * Logs the given event code to chrome.metricsPrivate.
- *
- * @param {number} eventCode event code to log.
- * @private
- */
- log_(eventCode) {
- chrome.metricsPrivate.recordValue(this.actionsMetric_, eventCode);
- }
+ /** Recorded when a stroke is made using touch. */
+ ANNOTATE_STROKE_DEVICE_TOUCH_FIRST: 29,
+ ANNOTATE_STROKE_DEVICE_TOUCH: 30,
- /**
- * Logs the given event code. Subsequent calls of this method with the same
- * event code have no effect on the this PDFMetrics instance.
- *
- * @param {number} eventCode event code to log.
- * @private
- */
- logOnlyFirstTime_(eventCode) {
- if (!this.firstEventLogged_.has(eventCode)) {
- this.log_(eventCode);
- this.firstEventLogged_.add(eventCode);
- }
- }
-};
+ /** Recorded when a stroke is made using mouse. */
+ ANNOTATE_STROKE_DEVICE_MOUSE_FIRST: 31,
+ ANNOTATE_STROKE_DEVICE_MOUSE: 32,
-window.PDFMetrics.UserAction = UserAction;
+ /** Recorded when a stroke is made using pen. */
+ ANNOTATE_STROKE_DEVICE_PEN_FIRST: 33,
+ ANNOTATE_STROKE_DEVICE_PEN: 34,
+
+ NUMBER_OF_ACTIONS: 35,
+};
-}());
+// Map from UserAction to the 'FIRST' action. These metrics are recorded
+// by PDFMetrics.log the first time each corresponding action occurs.
+/** @private Map<number, number> */
+PDFMetrics.firstMap_ = new Map([
+ [
+ PDFMetrics.UserAction.ROTATE,
+ PDFMetrics.UserAction.ROTATE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.FIT_TO_WIDTH,
+ PDFMetrics.UserAction.FIT_TO_WIDTH_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.FIT_TO_PAGE,
+ PDFMetrics.UserAction.FIT_TO_PAGE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.OPEN_BOOKMARKS_PANEL,
+ PDFMetrics.UserAction.OPEN_BOOKMARKS_PANEL_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.FOLLOW_BOOKMARK,
+ PDFMetrics.UserAction.FOLLOW_BOOKMARK_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.PAGE_SELECTOR_NAVIGATE,
+ PDFMetrics.UserAction.PAGE_SELECTOR_NAVIGATE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.SAVE,
+ PDFMetrics.UserAction.SAVE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.SAVE_WITH_ANNOTATION,
+ PDFMetrics.UserAction.SAVE_WITH_ANNOTATION_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.PRINT,
+ PDFMetrics.UserAction.PRINT_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ENTER_ANNOTATION_MODE,
+ PDFMetrics.UserAction.ENTER_ANNOTATION_MODE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.EXIT_ANNOTATION_MODE,
+ PDFMetrics.UserAction.EXIT_ANNOTATION_MODE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_PEN,
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_PEN_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_ERASER,
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_ERASER_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_HIGHLIGHTER,
+ PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_HIGHLIGHTER_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_TOUCH,
+ PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_TOUCH_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_MOUSE,
+ PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_MOUSE_FIRST,
+ ],
+ [
+ PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_PEN,
+ PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_PEN_FIRST,
+ ],
+]);
diff --git a/chromium/chrome/browser/resources/pdf/pdf_viewer.js b/chromium/chrome/browser/resources/pdf/pdf_viewer.js
index 03f0a28d55e..284afedf2d9 100644
--- a/chromium/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chromium/chrome/browser/resources/pdf/pdf_viewer.js
@@ -64,7 +64,8 @@ function shouldIgnoreKeyEvents(activeElement) {
}
return (
- activeElement.isContentEditable || activeElement.tagName == 'INPUT' ||
+ activeElement.isContentEditable ||
+ (activeElement.tagName == 'INPUT' && activeElement.type != 'radio') ||
activeElement.tagName == 'TEXTAREA');
}
@@ -135,12 +136,13 @@ function PDFViewer(browserApi) {
/** @private {boolean} */
this.hasEnteredAnnotationMode_ = false;
- /**
- * @type {!PDFMetrics}
- */
- this.metrics =
- (chrome.metricsPrivate ? new PDFMetricsImpl() : new PDFMetricsDummy());
- this.metrics.onDocumentOpened();
+ /** @private {boolean} */
+ this.hadPassword_ = false;
+
+ /** @private {boolean} */
+ this.canSerializeDocument_ = false;
+
+ PDFMetrics.record(PDFMetrics.UserAction.DOCUMENT_OPENED);
// Parse open pdf parameters.
this.paramsParser_ = new OpenPDFParamsParser(
@@ -175,7 +177,7 @@ function PDFViewer(browserApi) {
this.browserApi_.getZoomBehavior() == BrowserApi.ZoomBehavior.MANAGE ?
this.browserApi_.getDefaultZoom() :
1.0;
- this.viewport_ = new Viewport(
+ this.viewport_ = new ViewportImpl(
window, this.sizer_, this.viewportChanged_.bind(this),
() => this.currentController_.beforeZoom(),
() => {
@@ -255,9 +257,13 @@ function PDFViewer(browserApi) {
this.toolbar_.addEventListener('save', () => this.save());
this.toolbar_.addEventListener('print', () => this.print());
this.toolbar_.addEventListener(
- 'rotate-right', () => this.currentController_.rotateClockwise());
+ 'undo', () => this.currentController_.undo());
+ this.toolbar_.addEventListener(
+ 'redo', () => this.currentController_.redo());
+ this.toolbar_.addEventListener(
+ 'rotate-right', () => this.rotateClockwise());
this.toolbar_.addEventListener(
- 'annotation-mode-changed', e => this.annotationModeChanged_(e));
+ 'annotation-mode-toggled', e => this.annotationModeToggled_(e));
this.toolbar_.addEventListener(
'annotation-tool-changed',
e => this.inkController_.setAnnotationTool(e.detail.value));
@@ -268,9 +274,9 @@ function PDFViewer(browserApi) {
document.body.addEventListener('change-page', e => {
this.viewport_.goToPage(e.detail.page);
if (e.detail.origin == 'bookmark') {
- this.metrics.onFollowBookmark();
+ PDFMetrics.record(PDFMetrics.UserAction.FOLLOW_BOOKMARK);
} else if (e.detail.origin == 'pageselector') {
- this.metrics.onPageSelectorNavigation();
+ PDFMetrics.record(PDFMetrics.UserAction.PAGE_SELECTOR_NAVIGATE);
}
});
@@ -288,7 +294,7 @@ function PDFViewer(browserApi) {
document.body.addEventListener('dropdown-opened', e => {
if (e.detail == 'bookmarks') {
- this.metrics.onOpenBookmarksPanel();
+ PDFMetrics.record(PDFMetrics.UserAction.OPEN_BOOKMARKS_PANEL);
}
});
@@ -320,6 +326,11 @@ function PDFViewer(browserApi) {
// Request translated strings.
chrome.resourcesPrivate.getStrings('pdf', this.handleStrings_.bind(this));
+
+ // Listen for save commands from the browser.
+ if (chrome.mimeHandlerPrivate && chrome.mimeHandlerPrivate.onSave) {
+ chrome.mimeHandlerPrivate.onSave.addListener(this.onSave.bind(this));
+ }
}
PDFViewer.prototype = {
@@ -444,7 +455,7 @@ PDFViewer.prototype = {
return;
case 219: // Left bracket key.
if (e.ctrlKey) {
- this.currentController_.rotateCounterClockwise();
+ this.rotateCounterclockwise();
}
return;
case 220: // Backslash key.
@@ -454,7 +465,7 @@ PDFViewer.prototype = {
return;
case 221: // Right bracket key.
if (e.ctrlKey) {
- this.currentController_.rotateClockwise();
+ this.rotateClockwise();
}
return;
}
@@ -493,19 +504,31 @@ PDFViewer.prototype = {
/**
* Handles the annotation mode being toggled on or off.
*
- * @param {CustomEvent} e
+ * @param {!CustomEvent<{value: boolean}>} e
* @private
*/
- annotationModeChanged_: async function(e) {
+ annotationModeToggled_: async function(e) {
const annotationMode = e.detail.value;
if (annotationMode) {
- this.hasEnteredAnnotationMode_ = true;
- assert(this.currentController_ == this.pluginController_);
// Enter annotation mode.
+ assert(this.currentController_ == this.pluginController_);
// TODO(dstockwell): set plugin read-only, begin transition
this.updateProgress(0);
// TODO(dstockwell): handle save failure
const result = await this.pluginController_.save(true);
+ if (result.hasUnsavedChanges) {
+ assert(!loadTimeData.getBoolean('pdfFormSaveEnabled'));
+ try {
+ await $('form-warning').show();
+ } catch (e) {
+ // The user aborted entering annotation mode. Revert to the plugin.
+ this.toolbar_.annotationMode = false;
+ this.updateProgress(100);
+ return;
+ }
+ }
+ PDFMetrics.record(PDFMetrics.UserAction.ENTER_ANNOTATION_MODE);
+ this.hasEnteredAnnotationMode_ = true;
// TODO(dstockwell): feed real progress data from the Ink component
this.updateProgress(50);
await this.inkController_.load(result.fileName, result.dataToSave);
@@ -515,6 +538,7 @@ PDFViewer.prototype = {
this.updateProgress(100);
} else {
// Exit annotation mode.
+ PDFMetrics.record(PDFMetrics.UserAction.EXIT_ANNOTATION_MODE);
assert(this.currentController_ == this.inkController_);
// TODO(dstockwell): set ink read-only, begin transition
this.updateProgress(0);
@@ -548,7 +572,10 @@ PDFViewer.prototype = {
/**
* Request to change the viewport fitting type.
*
- * @param {CustomEvent} e Event received with the new FittingType as detail.
+ * @param {!CustomEvent<{
+ * fittingType: FittingType,
+ * userInitiated: boolean
+ * }>} e
* @private
*/
fitToChanged_: function(e) {
@@ -563,7 +590,7 @@ PDFViewer.prototype = {
}
if (e.detail.userInitiated) {
- this.metrics.onFitTo(e.detail.fittingType);
+ PDFMetrics.recordFitTo(e.detail.fittingType);
}
},
@@ -634,7 +661,7 @@ PDFViewer.prototype = {
goToPageAndXY_: function(origin, page, message) {
this.viewport_.goToPageAndXY(page, message.x, message.y);
if (origin == 'bookmark') {
- this.metrics.onFollowBookmark();
+ PDFMetrics.record(PDFMetrics.UserAction.FOLLOW_BOOKMARK);
}
},
@@ -728,6 +755,9 @@ PDFViewer.prototype = {
$('zoom-toolbar').strings = strings;
$('password-screen').strings = strings;
$('error-screen').strings = strings;
+ if ($('form-warning')) {
+ $('form-warning').strings = strings;
+ }
},
/**
@@ -1050,6 +1080,8 @@ PDFViewer.prototype = {
// 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.hadPassword_ = true;
+ this.updateAnnotationAvailable_();
this.passwordScreen_.show();
} else {
this.passwordScreen_.deny();
@@ -1095,8 +1127,9 @@ PDFViewer.prototype = {
* Sets document metadata from the current controller.
* @param {string} title
* @param {Array} bookmarks
+ * @param {boolean} canSerializeDocument
*/
- setDocumentMetadata: function(title, bookmarks) {
+ setDocumentMetadata: function(title, bookmarks, canSerializeDocument) {
if (title) {
document.title = title;
} else {
@@ -1107,6 +1140,8 @@ PDFViewer.prototype = {
this.toolbar_.docTitle = document.title;
this.toolbar_.bookmarks = this.bookmarks;
}
+ this.canSerializeDocument_ = canSerializeDocument;
+ this.updateAnnotationAvailable_();
},
/**
@@ -1126,9 +1161,28 @@ PDFViewer.prototype = {
},
/**
+ * An event handler for when the browser tells the PDF Viewer to perform a
+ * save.
+ *
+ * @param {string} streamUrl unique identifier for a PDF Viewer instance.
+ * @private
+ */
+ onSave: async function(streamUrl) {
+ if (streamUrl != this.browserApi_.getStreamInfo().streamUrl) {
+ return;
+ }
+
+ this.save();
+ },
+
+ /**
* Saves the current PDF document to disk.
*/
save: async function() {
+ PDFMetrics.record(PDFMetrics.UserAction.SAVE);
+ if (this.hasEnteredAnnotationMode_) {
+ PDFMetrics.record(PDFMetrics.UserAction.SAVE_WITH_ANNOTATION);
+ }
// If we have entered annotation mode we must require the local
// contents to ensure annotations are saved. Otherwise we would
// save the cached or remote copy without annotatios.
@@ -1148,9 +1202,20 @@ PDFViewer.prototype = {
chrome.fileSystem.chooseEntry(
{type: 'saveFile', suggestedName: fileName}, entry => {
+ if (chrome.runtime.lastError) {
+ if (chrome.runtime.lastError.message != 'User cancelled') {
+ console.log(
+ 'chrome.fileSystem.chooseEntry failed: ' +
+ chrome.runtime.lastError.message);
+ }
+ return;
+ }
entry.createWriter(writer => {
writer.write(
new Blob([result.dataToSave], {type: 'application/pdf'}));
+ // Unblock closing the window now that the user has saved
+ // successfully.
+ chrome.mimeHandlerPrivate.setShowBeforeUnloadDialog(false);
});
});
@@ -1159,8 +1224,52 @@ PDFViewer.prototype = {
},
print: async function() {
+ PDFMetrics.record(PDFMetrics.UserAction.PRINT);
await this.exitAnnotationMode_();
this.currentController_.print();
+ },
+
+ /**
+ * Updates the toolbar's annotation available flag depending on current
+ * conditions.
+ */
+ updateAnnotationAvailable_() {
+ let annotationAvailable = true;
+ if (this.viewport_.getClockwiseRotations() != 0) {
+ annotationAvailable = false;
+ }
+ if (this.hadPassword_) {
+ annotationAvailable = false;
+ }
+ if (!this.canSerializeDocument_) {
+ annotationAvailable = false;
+ }
+ this.toolbar_.annotationAvailable = annotationAvailable;
+ },
+
+ rotateClockwise() {
+ PDFMetrics.record(PDFMetrics.UserAction.ROTATE);
+ this.viewport_.rotateClockwise(1);
+ this.currentController_.rotateClockwise();
+ this.updateAnnotationAvailable_();
+ },
+
+ rotateCounterclockwise() {
+ PDFMetrics.record(PDFMetrics.UserAction.ROTATE);
+ this.viewport_.rotateClockwise(3);
+ this.currentController_.rotateCounterclockwise();
+ this.updateAnnotationAvailable_();
+ },
+
+ setHasUnsavedChanges: function() {
+ // Warn the user if they attempt to close the window without saving.
+ chrome.mimeHandlerPrivate.setShowBeforeUnloadDialog(true);
+ },
+
+ /** @param {UndoState} state */
+ setAnnotationUndoState(state) {
+ this.toolbar_.canUndoAnnotation = state.canUndo;
+ this.toolbar_.canRedoAnnotation = state.canRedo;
}
};
@@ -1193,7 +1302,7 @@ class ContentController {
* Rotates the document 90 degrees in the counter clockwise direction.
* @abstract
*/
- rotateCounterClockwise() {}
+ rotateCounterclockwise() {}
/**
* Triggers printing of the current document.
@@ -1201,6 +1310,16 @@ class ContentController {
print() {}
/**
+ * Undo an edit action.
+ */
+ undo() {}
+
+ /**
+ * Redo an edit action.
+ */
+ redo() {}
+
+ /**
* Requests that the current document be saved.
* @param {boolean} requireResult whether a response is required, otherwise
* the controller may save the document to disk internally.
@@ -1253,7 +1372,7 @@ class InkController extends ContentController {
}
/** @override */
- rotateCounterClockwise() {
+ rotateCounterclockwise() {
// TODO(dstockwell): implement rotation
}
@@ -1268,11 +1387,27 @@ class InkController extends ContentController {
}
/** @override */
+ undo() {
+ this.inkHost_.undo();
+ }
+
+ /** @override */
+ redo() {
+ this.inkHost_.redo();
+ }
+
+ /** @override */
load(filename, data) {
if (!this.inkHost_) {
this.inkHost_ = document.createElement('viewer-ink-host');
$('content').appendChild(this.inkHost_);
this.inkHost_.viewport = this.viewport_;
+ this.inkHost_.addEventListener('stroke-added', e => {
+ this.viewer_.setHasUnsavedChanges();
+ });
+ this.inkHost_.addEventListener('undo-state-changed', e => {
+ this.viewer_.setAnnotationUndoState(e.detail);
+ });
}
return this.inkHost_.load(filename, data);
}
@@ -1365,15 +1500,11 @@ class PluginController extends ContentController {
/** @override */
rotateClockwise() {
- this.viewer_.metrics.onRotation();
- this.viewport_.rotateClockwise(1);
this.postMessage({type: 'rotateClockwise'});
}
/** @override */
- rotateCounterClockwise() {
- this.viewer_.metrics.onRotation();
- this.viewport_.rotateClockwise(3);
+ rotateCounterclockwise() {
this.postMessage({type: 'rotateCounterclockwise'});
}
@@ -1421,7 +1552,7 @@ class PluginController extends ContentController {
this.viewer_.handleBeep();
break;
case 'documentDimensions':
- viewer.setDocumentDimensions(message.data);
+ this.viewer_.setDocumentDimensions(message.data);
break;
case 'email':
const href = 'mailto:' + message.data.to + '?cc=' + message.data.cc +
@@ -1458,7 +1589,8 @@ class PluginController extends ContentController {
break;
case 'metadata':
this.viewer_.setDocumentMetadata(
- message.data.title, message.data.bookmarks);
+ message.data.title, message.data.bookmarks,
+ message.data.canSerializeDocument);
break;
case 'setIsSelecting':
this.viewer_.setIsSelecting(message.data.isSelecting);
diff --git a/chromium/chrome/browser/resources/pdf/viewport.js b/chromium/chrome/browser/resources/pdf/viewport.js
index dce8d534ccd..02664b3fe33 100644
--- a/chromium/chrome/browser/resources/pdf/viewport.js
+++ b/chromium/chrome/browser/resources/pdf/viewport.js
@@ -2,37 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-/**
- * @typedef {{
- * x: number,
- * y: number
- * }}
- */
-let Point;
-
-/**
- * @typedef {{
- * x: (number|undefined),
- * y: (number|undefined),
- * }}
- */
-let PartialPoint;
/**
- * @typedef {{
- * x: number,
- * y: number,
- * width: number,
- * heigh: number,
- * }}
+ * Clamps the zoom factor (or page scale factor) to be within the limits.
+ *
+ * @param {number} factor The zoom/scale factor.
+ * @return {number} The factor clamped within the limits.
*/
-let Rect;
+function clampZoom(factor) {
+ return Math.max(
+ Viewport.ZOOM_FACTOR_RANGE.min,
+ Math.min(factor, Viewport.ZOOM_FACTOR_RANGE.max));
+}
/**
* Returns the height of the intersection of two rectangles.
*
- * @param {Rect} rect1 the first rect
- * @param {Rect} rect2 the second rect
+ * @param {!ViewportRect} rect1 the first rect
+ * @param {!ViewportRect} rect2 the second rect
* @return {number} the height of the intersection of the rects
*/
function getIntersectionHeight(rect1, rect2) {
@@ -45,9 +32,9 @@ function getIntersectionHeight(rect1, rect2) {
/**
* Computes vector between two points.
*
- * @param {!Object} p1 The first point.
- * @param {!Object} p2 The second point.
- * @return {!Object} The vector.
+ * @param {!Point} p1 The first point.
+ * @param {!Point} p2 The second point.
+ * @return {!Point} The vector.
*/
function vectorDelta(p1, p2) {
return {x: p2.x - p1.x, y: p2.y - p1.y};
@@ -61,133 +48,75 @@ function frameToPluginCoordinate(coordinateInFrame) {
};
}
-// TODO: convert Viewport to ES6 class syntax
-/**
- * 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 {Function} beforeZoomCallback is run before a change in zoom
- * @param {Function} afterZoomCallback is run after a change in zoom
- * @param {Function} setUserInitiatedCallback is run to indicate whether a zoom
- * event is user initiated.
- * @param {number} scrollbarWidth the width of scrollbars on the page
- * @param {number} defaultZoom The default zoom level.
- * @param {number} topToolbarHeight The number of pixels that should initially
- * be left blank above the document for the toolbar.
- * @constructor
- */
-function Viewport(
- window, sizer, viewportChangedCallback, beforeZoomCallback,
- afterZoomCallback, setUserInitiatedCallback, scrollbarWidth, defaultZoom,
- topToolbarHeight) {
- this.window_ = window;
- this.sizer_ = sizer;
- this.viewportChangedCallback_ = viewportChangedCallback;
- this.beforeZoomCallback_ = beforeZoomCallback;
- this.afterZoomCallback_ = afterZoomCallback;
- this.setUserInitiatedCallback_ = setUserInitiatedCallback;
- this.allowedToChangeZoom_ = false;
- this.internalZoom_ = 1;
- this.zoomManager_ = new InactiveZoomManager(this, 1);
- this.documentDimensions_ = null;
- this.pageDimensions_ = [];
- this.scrollbarWidth_ = scrollbarWidth;
- this.fittingType_ = FittingType.NONE;
- this.defaultZoom_ = defaultZoom;
- this.topToolbarHeight_ = topToolbarHeight;
- this.prevScale_ = 1;
- this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
- this.pinchPanVector_ = null;
- this.pinchCenter_ = null;
- this.firstPinchCenterInFrame_ = null;
- this.rotations_ = 0;
-
- window.addEventListener('scroll', this.updateViewport_.bind(this));
- window.addEventListener('resize', this.resizeWrapper_.bind(this));
-}
-
-/**
- * Enumeration of pinch states.
- * This should match PinchPhase enum in pdf/out_of_process_instance.h
- * @enum {number}
- */
-Viewport.PinchPhase = {
- PINCH_NONE: 0,
- PINCH_START: 1,
- PINCH_UPDATE_ZOOM_OUT: 2,
- PINCH_UPDATE_ZOOM_IN: 3,
- PINCH_END: 4
-};
-
-/**
- * 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. This should match the lists in
- * components/ui/zoom/page_zoom_constants.h and
- * chrome/browser/resources/settings/appearance_page/appearance_page.js
- */
-Viewport.ZOOM_FACTORS = [
- 0.25, 1 / 3, 0.5, 2 / 3, 0.75, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3,
- 4, 5
-];
-
-/**
- * The minimum and maximum range to be used to clip zoom factor.
- */
-Viewport.ZOOM_FACTOR_RANGE = {
- min: Viewport.ZOOM_FACTORS[0],
- max: Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]
-};
-
-/**
- * Clamps the zoom factor (or page scale factor) to be within the limits.
- *
- * @param {number} factor The zoom/scale factor.
- * @return {number} The factor clamped within the limits.
- */
-Viewport.clampZoom = function(factor) {
- return Math.max(
- Viewport.ZOOM_FACTOR_RANGE.min,
- Math.min(factor, Viewport.ZOOM_FACTOR_RANGE.max));
-};
-
-/**
- * The width of the page shadow around pages in pixels.
- */
-Viewport.PAGE_SHADOW = {
- top: 3,
- bottom: 7,
- left: 5,
- right: 5
-};
+/** @implements {Viewport} */
+class ViewportImpl {
+ /**
+ * 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 {Function} beforeZoomCallback is run before a change in zoom
+ * @param {Function} afterZoomCallback is run after a change in zoom
+ * @param {Function} setUserInitiatedCallback is run to indicate whether a
+ * zoom event is user initiated.
+ * @param {number} scrollbarWidth the width of scrollbars on the page
+ * @param {number} defaultZoom The default zoom level.
+ * @param {number} topToolbarHeight The number of pixels that should initially
+ * be left blank above the document for the toolbar.
+ */
+ constructor(
+ window, sizer, viewportChangedCallback, beforeZoomCallback,
+ afterZoomCallback, setUserInitiatedCallback, scrollbarWidth, defaultZoom,
+ topToolbarHeight) {
+ this.window_ = window;
+ this.sizer_ = sizer;
+ this.viewportChangedCallback_ = viewportChangedCallback;
+ this.beforeZoomCallback_ = beforeZoomCallback;
+ this.afterZoomCallback_ = afterZoomCallback;
+ this.setUserInitiatedCallback_ = setUserInitiatedCallback;
+ this.allowedToChangeZoom_ = false;
+ this.internalZoom_ = 1;
+ this.zoomManager_ = new InactiveZoomManager(this, 1);
+ /** @private {?DocumentDimensions} */
+ this.documentDimensions_ = null;
+ /** @private {Array<ViewportRect>} */
+ this.pageDimensions_ = [];
+ this.scrollbarWidth_ = scrollbarWidth;
+ this.fittingType_ = FittingType.NONE;
+ this.defaultZoom_ = defaultZoom;
+ this.topToolbarHeight_ = topToolbarHeight;
+ this.prevScale_ = 1;
+ this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
+ this.pinchPanVector_ = null;
+ this.pinchCenter_ = null;
+ /** @private {?Point} */
+ this.firstPinchCenterInFrame_ = null;
+ this.rotations_ = 0;
+ // TODO(dstockwell): why isn't this private?
+ this.oldCenterInContent = null;
+ this.keepContentCentered_ = null;
-Viewport.prototype = {
+ window.addEventListener('scroll', this.updateViewport_.bind(this));
+ window.addEventListener('resize', this.resizeWrapper_.bind(this));
+ }
/**
* @param {number} n the number of clockwise 90-degree rotations to
* increment by.
*/
- rotateClockwise: function(n) {
+ rotateClockwise(n) {
this.rotations_ = (this.rotations_ + n) % 4;
- },
+ }
/**
* @return {number} the number of clockwise 90-degree rotations that have been
* applied.
*/
- getClockwiseRotations: function() {
+ getClockwiseRotations() {
return this.rotations_;
- },
+ }
/**
* Converts a page position (e.g. the location of a bookmark) to a screen
@@ -197,7 +126,7 @@ Viewport.prototype = {
* @param {Point} point The position on `page`.
* @return The screen position.
*/
- convertPageToScreen: function(page, point) {
+ convertPageToScreen(page, point) {
const dimensions = this.getPageInsetDimensions(page);
// width & height are already rotated.
@@ -231,7 +160,7 @@ Viewport.prototype = {
x: result.x + Viewport.PAGE_SHADOW.left,
y: result.y + Viewport.PAGE_SHADOW.top,
};
- },
+ }
/**
@@ -244,7 +173,7 @@ Viewport.prototype = {
* @return {Object} A dictionary with scaled 'width'/'height' of the document.
* @private
*/
- getZoomedDocumentDimensions_: function(zoom) {
+ getZoomedDocumentDimensions_(zoom) {
if (!this.documentDimensions_) {
return null;
}
@@ -252,30 +181,23 @@ Viewport.prototype = {
width: Math.round(this.documentDimensions_.width * zoom),
height: Math.round(this.documentDimensions_.height * zoom)
};
- },
+ }
- /**
- * Returns the document dimensions.
- *
- * @return {Point} A dictionary with the 'width'/'height' of the document.
- */
- getDocumentDimensions: function() {
+ /** @override */
+ getDocumentDimensions() {
return {
width: this.documentDimensions_.width,
height: this.documentDimensions_.height
};
- },
+ }
/**
- * 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.
+ * @return {{horizontal: boolean, vertical: boolean}} whether horizontal or
+ * vertical scrollbars are needed.
* @private
*/
- documentNeedsScrollbars_: function(zoom) {
+ documentNeedsScrollbars_(zoom) {
const zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
if (!zoomedDimensions) {
return {horizontal: false, vertical: false};
@@ -294,7 +216,7 @@ Viewport.prototype = {
vertical: zoomedDimensions.height + this.topToolbarHeight_ >
this.window_.innerHeight
};
- },
+ }
/**
* Returns true if the document needs scrollbars at the current zoom level.
@@ -303,50 +225,50 @@ Viewport.prototype = {
* indicating if the horizontal and vertical scrollbars are needed
* respectively.
*/
- documentHasScrollbars: function() {
+ documentHasScrollbars() {
return this.documentNeedsScrollbars_(this.zoom);
- },
+ }
/**
* Helper function called when the zoomed document size changes.
*
* @private
*/
- contentSizeChanged_: function() {
+ contentSizeChanged_() {
const zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom);
if (zoomedDimensions) {
this.sizer_.style.width = zoomedDimensions.width + 'px';
this.sizer_.style.height =
zoomedDimensions.height + this.topToolbarHeight_ + 'px';
}
- },
+ }
/**
* Called when the viewport should be updated.
*
* @private
*/
- updateViewport_: function() {
+ updateViewport_() {
this.viewportChangedCallback_();
- },
+ }
/**
* Called when the browser window size changes.
*
* @private
*/
- resizeWrapper_: function() {
+ resizeWrapper_() {
this.setUserInitiatedCallback_(false);
this.resize_();
this.setUserInitiatedCallback_(true);
- },
+ }
/**
* Called when the viewport size changes.
*
* @private
*/
- resize_: function() {
+ resize_() {
if (this.fittingType_ == FittingType.FIT_TO_PAGE) {
this.fitToPageInternal_(false);
} else if (this.fittingType_ == FittingType.FIT_TO_WIDTH) {
@@ -358,30 +280,26 @@ Viewport.prototype = {
} else {
this.updateViewport_();
}
- },
+ }
- /**
- * @type {Point} the scroll position of the viewport.
- */
+ /** @override */
get position() {
return {
x: this.window_.pageXOffset,
y: this.window_.pageYOffset - this.topToolbarHeight_
};
- },
+ }
/**
* Scroll the viewport to the specified position.
*
- * @type {Point} position The position to scroll to.
+ * @param {Point} position The position to scroll to.
*/
set position(position) {
this.window_.scrollTo(position.x, position.y + this.topToolbarHeight_);
- },
+ }
- /**
- * @type {Object} the size of the viewport excluding scrollbars.
- */
+ /** @override */
get size() {
const needsScrollbars = this.documentNeedsScrollbars_(this.zoom);
const scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0;
@@ -391,14 +309,12 @@ Viewport.prototype = {
width: this.window_.innerWidth - scrollbarWidth,
height: this.window_.innerHeight - scrollbarHeight
};
- },
+ }
- /**
- * @type {number} the zoom level of the viewport.
- */
+ /** @override */
get zoom() {
return this.zoomManager_.applyBrowserZoom(this.internalZoom_);
- },
+ }
/**
* Set the zoom manager.
@@ -407,30 +323,31 @@ Viewport.prototype = {
*/
set zoomManager(manager) {
this.zoomManager_ = manager;
- },
+ }
/**
- * @type {Viewport.PinchPhase} The phase of the current pinch gesture for
+ * @return {Viewport.PinchPhase} The phase of the current pinch gesture for
* the viewport.
*/
get pinchPhase() {
return this.pinchPhase_;
- },
+ }
/**
- * @type {Object} The panning caused by the current pinch gesture (as
+ * @return {Object} The panning caused by the current pinch gesture (as
* the deltas of the x and y coordinates).
*/
get pinchPanVector() {
return this.pinchPanVector_;
- },
+ }
/**
- * @type {Object} The coordinates of the center of the current pinch gesture.
+ * @return {Object} The coordinates of the center of the current pinch
+ * gesture.
*/
get pinchCenter() {
return this.pinchCenter_;
- },
+ }
/**
* Used to wrap a function that might perform zooming on the viewport. This is
@@ -441,13 +358,13 @@ Viewport.prototype = {
* @param {Function} f Function to wrap
* @private
*/
- mightZoom_: function(f) {
+ mightZoom_(f) {
this.beforeZoomCallback_();
this.allowedToChangeZoom_ = true;
f();
this.allowedToChangeZoom_ = false;
this.afterZoomCallback_();
- },
+ }
/**
* Sets the zoom of the viewport.
@@ -455,7 +372,7 @@ Viewport.prototype = {
* @param {number} newZoom the zoom level to zoom to.
* @private
*/
- setZoomInternal_: function(newZoom) {
+ setZoomInternal_(newZoom) {
assert(
this.allowedToChangeZoom_,
'Called Viewport.setZoomInternal_ without calling ' +
@@ -473,7 +390,7 @@ Viewport.prototype = {
x: currentScrollPos.x * this.zoom,
y: currentScrollPos.y * this.zoom
};
- },
+ }
/**
* Sets the zoom of the viewport.
@@ -483,12 +400,12 @@ Viewport.prototype = {
* @param {!Object} center The pinch center in content coordinates.
* @private
*/
- setPinchZoomInternal_: function(scaleDelta, center) {
+ setPinchZoomInternal_(scaleDelta, center) {
assert(
this.allowedToChangeZoom_,
'Called Viewport.setPinchZoomInternal_ without calling ' +
'Viewport.mightZoom_.');
- this.internalZoom_ = Viewport.clampZoom(this.internalZoom_ * scaleDelta);
+ this.internalZoom_ = clampZoom(this.internalZoom_ * scaleDelta);
const newCenterInContent = this.frameToContent(center);
const delta = {
@@ -505,7 +422,7 @@ Viewport.prototype = {
this.contentSizeChanged_();
// Scroll to the scaled scroll position.
this.position = {x: currentScrollPos.x, y: currentScrollPos.y};
- },
+ }
/**
* Converts a point from frame to content coordinates.
@@ -514,35 +431,30 @@ Viewport.prototype = {
* @return {!Object} The content coordinates.
* @private
*/
- frameToContent: function(framePoint) {
+ frameToContent(framePoint) {
// TODO(mcnee) Add a helper Point class to avoid duplicating operations
// on plain {x,y} objects.
return {
x: (framePoint.x + this.position.x) / this.zoom,
y: (framePoint.y + this.position.y) / this.zoom
};
- },
+ }
/**
* Sets the zoom to the given zoom level.
*
* @param {number} newZoom the zoom level to zoom to.
*/
- setZoom: function(newZoom) {
+ setZoom(newZoom) {
this.fittingType_ = FittingType.NONE;
this.mightZoom_(() => {
- this.setZoomInternal_(Viewport.clampZoom(newZoom));
+ this.setZoomInternal_(clampZoom(newZoom));
this.updateViewport_();
});
- },
+ }
- /**
- * Gets notified of the browser zoom changing seperately from the
- * internal zoom.
- *
- * @param {number} oldBrowserZoom the previous value of the browser zoom.
- */
- updateZoomFromBrowserChange: function(oldBrowserZoom) {
+ /** @override */
+ updateZoomFromBrowserChange(oldBrowserZoom) {
this.mightZoom_(() => {
// Record the scroll position (relative to the top-left of the window).
const oldZoom = oldBrowserZoom * this.internalZoom_;
@@ -558,21 +470,21 @@ Viewport.prototype = {
};
this.updateViewport_();
});
- },
+ }
/**
- * @type {number} the width of scrollbars in the viewport in pixels.
+ * @return {number} the width of scrollbars in the viewport in pixels.
*/
get scrollbarWidth() {
return this.scrollbarWidth_;
- },
+ }
/**
- * @type {FittingType} the fitting type the viewport is currently in.
+ * @return {FittingType} the fitting type the viewport is currently in.
*/
get fittingType() {
return this.fittingType_;
- },
+ }
/**
* Get the which page is at a given y position.
@@ -581,7 +493,7 @@ Viewport.prototype = {
* @return {number} the index of a page overlapping the given y-coordinate.
* @private
*/
- getPageAtY_: function(y) {
+ getPageAtY_(y) {
let min = 0;
let max = this.pageDimensions_.length - 1;
while (max >= min) {
@@ -607,12 +519,9 @@ Viewport.prototype = {
}
}
return 0;
- },
+ }
- /**
- * @param {Point} point
- * @return {boolean} Whether |point| (in screen coordinates) is inside a page
- */
+ /** @override */
isPointInsidePage(point) {
const zoom = this.zoom;
const size = this.size;
@@ -632,7 +541,7 @@ Viewport.prototype = {
const minX = (outerWidth - pageWidth) / 2;
const maxX = outerWidth - minX;
return x >= minX && x <= maxX;
- },
+ }
/**
* Returns the page with the greatest proportion of its height in the current
@@ -640,7 +549,7 @@ Viewport.prototype = {
*
* @return {number} the index of the most visible page.
*/
- getMostVisiblePage: function() {
+ getMostVisiblePage() {
const firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom);
if (firstVisiblePage == this.pageDimensions_.length - 1) {
return firstVisiblePage;
@@ -664,7 +573,7 @@ Viewport.prototype = {
return firstVisiblePage + 1;
}
return firstVisiblePage;
- },
+ }
/**
* Compute the zoom level for fit-to-page, fit-to-width or fit-to-height.
@@ -679,7 +588,7 @@ Viewport.prototype = {
* @return {number} the internal zoom to set
* @private
*/
- computeFittingZoom_: function(pageDimensions, fitWidth, fitHeight) {
+ computeFittingZoom_(pageDimensions, fitWidth, fitHeight) {
assert(
fitWidth || fitHeight,
'Invalid parameters. At least one of fitWidth and fitHeight must be ' +
@@ -730,7 +639,7 @@ Viewport.prototype = {
pageDimensions.height);
return this.zoomManager_.internalZoomComponent(zoom);
- },
+ }
/**
* Compute a zoom level given the dimensions to fit and the actual numbers
@@ -747,7 +656,7 @@ Viewport.prototype = {
* @return {number} the internal zoom to set
* @private
*/
- computeFittingZoomGivenDimensions_: function(
+ computeFittingZoomGivenDimensions_(
fitWidth, fitHeight, windowWidth, windowHeight, pageWidth, pageHeight) {
// Assumes at least one of {fitWidth, fitHeight} is set.
let zoomWidth;
@@ -772,12 +681,12 @@ Viewport.prototype = {
}
return Math.max(zoom, 0);
- },
+ }
/**
* Zoom the viewport so that the page width consumes the entire viewport.
*/
- fitToWidth: function() {
+ fitToWidth() {
this.mightZoom_(() => {
this.fittingType_ = FittingType.FIT_TO_WIDTH;
if (!this.documentDimensions_) {
@@ -789,7 +698,7 @@ Viewport.prototype = {
this.computeFittingZoom_(this.documentDimensions_, true, false));
this.updateViewport_();
});
- },
+ }
/**
* Zoom the viewport so that the page height consumes the entire viewport.
@@ -799,7 +708,7 @@ Viewport.prototype = {
* should remain at the current scroll position.
* @private
*/
- fitToHeightInternal_: function(scrollToTopOfPage) {
+ fitToHeightInternal_(scrollToTopOfPage) {
this.mightZoom_(() => {
this.fittingType_ = FittingType.FIT_TO_HEIGHT;
if (!this.documentDimensions_) {
@@ -818,14 +727,14 @@ Viewport.prototype = {
}
this.updateViewport_();
});
- },
+ }
/**
* Zoom the viewport so that the page height consumes the entire viewport.
*/
- fitToHeight: function() {
+ fitToHeight() {
this.fitToHeightInternal_(true);
- },
+ }
/**
* Zoom the viewport so that a page consumes as much as possible of the it.
@@ -835,7 +744,7 @@ Viewport.prototype = {
* should remain at the current scroll position.
* @private
*/
- fitToPageInternal_: function(scrollToTopOfPage) {
+ fitToPageInternal_(scrollToTopOfPage) {
this.mightZoom_(() => {
this.fittingType_ = FittingType.FIT_TO_PAGE;
if (!this.documentDimensions_) {
@@ -853,20 +762,20 @@ Viewport.prototype = {
}
this.updateViewport_();
});
- },
+ }
/**
* Zoom the viewport so that a page consumes the entire viewport. Also scrolls
* the viewport to the top of the current page.
*/
- fitToPage: function() {
+ fitToPage() {
this.fitToPageInternal_(true);
- },
+ }
/**
* Zoom the viewport to the default zoom policy.
*/
- fitToNone: function() {
+ fitToNone() {
this.mightZoom_(() => {
this.fittingType_ = FittingType.NONE;
if (!this.documentDimensions_) {
@@ -877,12 +786,12 @@ Viewport.prototype = {
this.computeFittingZoom_(this.documentDimensions_, true, false)));
this.updateViewport_();
});
- },
+ }
/**
* Zoom out to the next predefined zoom level.
*/
- zoomOut: function() {
+ zoomOut() {
this.mightZoom_(() => {
this.fittingType_ = FittingType.NONE;
let nextZoom = Viewport.ZOOM_FACTORS[0];
@@ -894,12 +803,12 @@ Viewport.prototype = {
this.setZoomInternal_(nextZoom);
this.updateViewport_();
});
- },
+ }
/**
* Zoom in to the next predefined zoom level.
*/
- zoomIn: function() {
+ zoomIn() {
this.mightZoom_(() => {
this.fittingType_ = FittingType.NONE;
let nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1];
@@ -911,26 +820,28 @@ Viewport.prototype = {
this.setZoomInternal_(nextZoom);
this.updateViewport_();
});
- },
+ }
/**
* Pinch zoom event handler.
*
* @param {!Object} e The pinch event.
*/
- pinchZoom: function(e) {
+ pinchZoom(e) {
this.mightZoom_(() => {
this.pinchPhase_ = e.direction == 'out' ?
Viewport.PinchPhase.PINCH_UPDATE_ZOOM_OUT :
Viewport.PinchPhase.PINCH_UPDATE_ZOOM_IN;
const scaleDelta = e.startScaleRatio / this.prevScale_;
- this.pinchPanVector_ =
- vectorDelta(e.center, this.firstPinchCenterInFrame_);
+ if (this.firstPinchCenterInFrame_ != null) {
+ this.pinchPanVector_ =
+ vectorDelta(e.center, this.firstPinchCenterInFrame_);
+ }
const needsScrollbars =
this.documentNeedsScrollbars_(this.zoomManager_.applyBrowserZoom(
- Viewport.clampZoom(this.internalZoom_ * scaleDelta)));
+ clampZoom(this.internalZoom_ * scaleDelta)));
this.pinchCenter_ = e.center;
@@ -955,9 +866,10 @@ Viewport.prototype = {
this.updateViewport_();
this.prevScale_ = e.startScaleRatio;
});
- },
+ }
- pinchZoomStart: function(e) {
+ /** @param {!Object} e The pinch event. */
+ pinchZoomStart(e) {
this.pinchPhase_ = Viewport.PinchPhase.PINCH_START;
this.prevScale_ = 1;
this.oldCenterInContent =
@@ -968,9 +880,10 @@ Viewport.prototype = {
// We keep track of begining of the pinch.
// By doing so we will be able to compute the pan distance.
this.firstPinchCenterInFrame_ = e.center;
- },
+ }
- pinchZoomEnd: function(e) {
+ /** @param {!Object} e The pinch event. */
+ pinchZoomEnd(e) {
this.mightZoom_(() => {
this.pinchPhase_ = Viewport.PinchPhase.PINCH_END;
const scaleDelta = e.startScaleRatio / this.prevScale_;
@@ -984,16 +897,16 @@ Viewport.prototype = {
this.pinchPanVector_ = null;
this.pinchCenter_ = null;
this.firstPinchCenterInFrame_ = null;
- },
+ }
/**
* Go to the given page index.
*
* @param {number} page the index of the page to go to. zero-based.
*/
- goToPage: function(page) {
+ goToPage(page) {
this.goToPageAndXY(page, 0, 0);
- },
+ }
/**
* Go to the given y position in the given page index.
@@ -1002,7 +915,7 @@ Viewport.prototype = {
* @param {number} x the x position in the page to go to.
* @param {number} y the y position in the page to go to.
*/
- goToPageAndXY: function(page, x, y) {
+ goToPageAndXY(page, x, y) {
this.mightZoom_(() => {
if (this.pageDimensions_.length === 0) {
return;
@@ -1027,14 +940,15 @@ Viewport.prototype = {
};
this.updateViewport_();
});
- },
+ }
/**
* Set the dimensions of the document.
*
- * @param {Object} documentDimensions the dimensions of the document
+ * @param {DocumentDimensions} documentDimensions the dimensions of the
+ * document
*/
- setDocumentDimensions: function(documentDimensions) {
+ setDocumentDimensions(documentDimensions) {
this.mightZoom_(() => {
const initialDimensions = !this.documentDimensions_;
this.documentDimensions_ = documentDimensions;
@@ -1048,13 +962,13 @@ Viewport.prototype = {
this.contentSizeChanged_();
this.resize_();
});
- },
+ }
/**
* @param {number} page
- * @return {Rect} The bounds for page `page` minus the shadows.
+ * @return {ViewportRect} The bounds for page `page` minus the shadows.
*/
- getPageInsetDimensions: function(page) {
+ getPageInsetDimensions(page) {
const pageDimensions = this.pageDimensions_[page];
const shadow = Viewport.PAGE_SHADOW;
return {
@@ -1063,7 +977,7 @@ Viewport.prototype = {
width: pageDimensions.width - shadow.left - shadow.right,
height: pageDimensions.height - shadow.top - shadow.bottom,
};
- },
+ }
/**
* Get the coordinates of the page contents (excluding the page shadow)
@@ -1072,7 +986,7 @@ Viewport.prototype = {
* @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) {
+ getPageScreenRect(page) {
if (!this.documentDimensions_) {
return {x: 0, y: 0, width: 0, height: 0};
}
@@ -1102,7 +1016,7 @@ Viewport.prototype = {
width: insetDimensions.width * this.zoom,
height: insetDimensions.height * this.zoom
};
- },
+ }
/**
* Check if the current fitting type is a paged mode.
@@ -1112,18 +1026,18 @@ Viewport.prototype = {
*
* @return {boolean} Whether the current fitting type is a paged mode.
*/
- isPagedMode: function(page) {
+ isPagedMode() {
return (
this.fittingType_ == FittingType.FIT_TO_PAGE ||
this.fittingType_ == FittingType.FIT_TO_HEIGHT);
- },
+ }
/**
* Scroll the viewport to the specified position.
*
- * @param {!PartialPoint} point The position to which to move the viewport.
+ * @param {!Point} point The position to which to move the viewport.
*/
- scrollTo: function(point) {
+ scrollTo(point) {
let changed = false;
const newPosition = this.position;
if (point.x !== undefined && point.x != newPosition.x) {
@@ -1138,17 +1052,17 @@ Viewport.prototype = {
if (changed) {
this.position = newPosition;
}
- },
+ }
/**
* Scroll the viewport by the specified delta.
*
* @param {!Point} delta The delta by which to move the viewport.
*/
- scrollBy: function(delta) {
+ scrollBy(delta) {
const newPosition = this.position;
newPosition.x += delta.x;
newPosition.y += delta.y;
this.scrollTo(newPosition);
}
-};
+}
diff --git a/chromium/chrome/browser/resources/pdf/viewport_interface.js b/chromium/chrome/browser/resources/pdf/viewport_interface.js
new file mode 100644
index 00000000000..c9f99aceb2f
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/viewport_interface.js
@@ -0,0 +1,136 @@
+// Copyright 2019 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.
+
+/**
+ * @typedef {{
+ * width: number,
+ * height: number,
+ * pageDimensions: Array<ViewportRect>,
+ * }}
+ */
+let DocumentDimensions;
+
+/**
+ * @typedef {{
+ * x: number,
+ * y: number
+ * }}
+ */
+let Point;
+
+/**
+ * @typedef {{
+ * width: number,
+ * height: number,
+ * }}
+ */
+let Size;
+
+/**
+ * @typedef {{
+ * x: number,
+ * y: number,
+ * width: number,
+ * height: number,
+ * }}
+ */
+let ViewportRect;
+
+/**
+ * @interface
+ */
+class Viewport {
+ /**
+ * Returns the document dimensions.
+ *
+ * @return {!Size} A dictionary with the 'width'/'height' of the document.
+ */
+ getDocumentDimensions() {}
+
+ /**
+ * @return {!Point} the scroll position of the viewport.
+ */
+ get position() {}
+
+ /**
+ * @return {!Size} the size of the viewport excluding scrollbars.
+ */
+ get size() {}
+
+ /**
+ * @return {number} the zoom level of the viewport.
+ */
+ get zoom() {}
+
+ /**
+ * Sets the zoom to the given zoom level.
+ *
+ * @param {number} newZoom the zoom level to zoom to.
+ */
+ setZoom(newZoom) {}
+
+ /**
+ * Gets notified of the browser zoom changing separately from the
+ * internal zoom.
+ *
+ * @param {number} oldBrowserZoom the previous value of the browser zoom.
+ */
+ updateZoomFromBrowserChange(oldBrowserZoom) {}
+
+ /**
+ * @param {!Point} point
+ * @return {boolean} Whether |point| (in screen coordinates) is inside a page
+ */
+ isPointInsidePage(point) {}
+}
+
+/**
+ * Enumeration of pinch states.
+ * This should match PinchPhase enum in pdf/out_of_process_instance.h
+ * @enum {number}
+ */
+Viewport.PinchPhase = {
+ PINCH_NONE: 0,
+ PINCH_START: 1,
+ PINCH_UPDATE_ZOOM_OUT: 2,
+ PINCH_UPDATE_ZOOM_IN: 3,
+ PINCH_END: 4
+};
+
+/**
+ * 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. This should match the lists in
+ * components/ui/zoom/page_zoom_constants.h and
+ * chrome/browser/resources/settings/appearance_page/appearance_page.js
+ */
+Viewport.ZOOM_FACTORS = [
+ 0.25, 1 / 3, 0.5, 2 / 3, 0.75, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3,
+ 4, 5
+];
+
+/**
+ * The minimum and maximum range to be used to clip zoom factor.
+ */
+Viewport.ZOOM_FACTOR_RANGE = {
+ min: Viewport.ZOOM_FACTORS[0],
+ max: Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]
+};
+
+/**
+ * The width of the page shadow around pages in pixels.
+ */
+Viewport.PAGE_SHADOW = {
+ top: 3,
+ bottom: 7,
+ left: 5,
+ right: 5
+};