diff options
Diffstat (limited to 'chromium/chrome/browser/resources/pdf')
23 files changed, 964 insertions, 753 deletions
diff --git a/chromium/chrome/browser/resources/pdf/BUILD.gn b/chromium/chrome/browser/resources/pdf/BUILD.gn index 1483ff4a9a0..89a0223ca86 100644 --- a/chromium/chrome/browser/resources/pdf/BUILD.gn +++ b/chromium/chrome/browser/resources/pdf/BUILD.gn @@ -14,6 +14,9 @@ group("closure_compile") { "elements/viewer-page-indicator:closure_compile", "elements/viewer-page-selector:closure_compile", "elements/viewer-password-screen:closure_compile", + "elements/viewer-pdf-toolbar:closure_compile", + "elements/viewer-toolbar-dropdown:closure_compile", + "elements/viewer-zoom-toolbar:closure_compile", ] if (is_chromeos) { deps += [ @@ -54,17 +57,12 @@ 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", + ":pdf_fitting_type", ":zoom_manager", + "//ui/webui/resources/js:event_tracker", "//ui/webui/resources/js:util", ] externs_list = [ "$externs_path/pending.js" ] @@ -73,7 +71,7 @@ js_library("viewport") { js_library("zoom_manager") { deps = [ ":browser_api", - ":viewport_interface", + "//ui/webui/resources/js/cr:event_target", ] } @@ -81,16 +79,23 @@ js_library("metrics") { externs_list = [ "$externs_path/metrics_private.js" ] } +js_library("navigator") { + deps = [ + ":open_pdf_params_parser", + ":viewport", + ] +} + js_type_check("pdf_resources") { deps = [ ":browser_api", ":gesture_detector", ":metrics", + ":navigator", ":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/viewer-bookmark/viewer-bookmark.html b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html index ae14eeb289f..004aa1b1677 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html @@ -49,21 +49,21 @@ transform: rotate(180deg); } - :host([children-shown]) #expand { + :host([children-shown_]) #expand { transform: rotate(90deg); } </style> - <div id="item" on-click="onClick"> + <div id="item" on-click="onClick_"> <paper-ripple></paper-ripple> <cr-icon-button id="expand" iron-icon="cr:chevron-right" - on-click="toggleChildren"></cr-icon-button> + on-click="toggleChildren_"></cr-icon-button> <span id="title" tabindex="0">{{bookmark.title}}</span> </div> <!-- dom-if will stamp the complex bookmark tree lazily as individual nodes are opened. --> - <template is="dom-if" if="[[childrenShown]]" id="sub-bookmarks"> + <template is="dom-if" if="[[childrenShown_]]"> <template is="dom-repeat" items="[[bookmark.children]]"> - <viewer-bookmark bookmark="{{item}}" depth="{{childDepth}}"> + <viewer-bookmark bookmark="{{item}}" depth="[[childDepth_]]"> </viewer-bookmark> </template> </template> diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js index 160191b6ecb..f1cce48abaa 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js @@ -15,9 +15,9 @@ * structure. * @typedef {{ * title: string, - * page: number, - * y: number, - * uri: string, + * page: (number | undefined), + * y: (number | undefined), + * uri: (string | undefined), * children: !Array<!Bookmark> * }} */ @@ -32,13 +32,25 @@ Polymer({ properties: { /** @type {Bookmark} */ - bookmark: {type: Object, observer: 'bookmarkChanged_'}, - - depth: {type: Number, observer: 'depthChanged'}, - - childDepth: Number, - - childrenShown: {type: Boolean, reflectToAttribute: true, value: false}, + bookmark: { + type: Object, + observer: 'bookmarkChanged_', + }, + + depth: { + type: Number, + observer: 'depthChanged_' + }, + + /** @private */ + childDepth_: Number, + + /** @private */ + childrenShown_: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, /** @type {?HTMLElement} The target for the key bindings below. */ keyEventTarget: Object, @@ -53,18 +65,21 @@ Polymer({ this.keyEventTarget = this.$.item; }, + /** @private */ bookmarkChanged_: function() { this.$.expand.style.visibility = this.bookmark.children.length > 0 ? 'visible' : 'hidden'; }, - depthChanged: function() { - this.childDepth = this.depth + 1; + /** @private */ + depthChanged_: function() { + this.childDepth_ = this.depth + 1; this.$.item.style.paddingInlineStart = (this.depth * BOOKMARK_INDENT) + 'px'; }, - onClick: function() { + /** @private */ + onClick_: function() { if (this.bookmark.hasOwnProperty('page')) { if (this.bookmark.hasOwnProperty('y')) { this.fire('change-page-and-xy', { @@ -82,25 +97,37 @@ Polymer({ } }, + /** + * @param {!KeyboardEvent} e + * @private + */ onEnter_: function(e) { // Don't allow events which have propagated up from the expand button to // trigger a click. if (e.detail.keyboardEvent.target != this.$.expand) { - this.onClick(); + this.onClick_(); } }, + /** + * @param {!KeyboardEvent} e + * @private + */ onSpace_: function(e) { // cr-icon-button stops propagation of space events, so there's no need // to check the event source here. - this.onClick(); + this.onClick_(); // Prevent default space scroll behavior. e.detail.keyboardEvent.preventDefault(); }, - toggleChildren: function(e) { - this.childrenShown = !this.childrenShown; - e.stopPropagation(); // Prevent the above onClick handler from firing. + /** + * @param {!Event} e + * @private + */ + toggleChildren_: function(e) { + this.childrenShown_ = !this.childrenShown_; + e.stopPropagation(); // Prevent the above onClick_ handler from firing. } }); })(); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.html b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.html deleted file mode 100644 index 2277ec4d941..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.html +++ /dev/null @@ -1,12 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> - -<link rel="import" href="../viewer-bookmark/viewer-bookmark.html"> - -<dom-module id="viewer-bookmarks-content"> - <template> - <template is="dom-repeat" items="[[bookmarks]]"> - <viewer-bookmark bookmark="{{item}}" depth="0"></viewer-bookmark> - </template> - </template> - <script src="viewer-bookmarks-content.js"></script> -</dom-module> diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.js b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.js deleted file mode 100644 index 32f3fd320d5..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.js +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2015 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-bookmarks-content'}); 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 6bb2e4c68ed..285edf8a35d 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 @@ -47,7 +47,7 @@ Polymer({ /** @private {boolean} */ penMode_: false, - /** @type {Viewport} */ + /** @type {?Viewport} */ viewport: null, /** @type {?AnnotationTool} */ @@ -247,7 +247,7 @@ Polymer({ const viewport = this.viewport; const pos = viewport.position; const size = viewport.size; - const zoom = viewport.zoom; + const zoom = viewport.getZoom(); const documentWidth = viewport.getDocumentDimensions().width * zoom; // Adjust for page shadows. const y = pos.y - Viewport.PAGE_SHADOW.top * zoom; diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/BUILD.gn new file mode 100644 index 00000000000..fdc650f55b9 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/BUILD.gn @@ -0,0 +1,20 @@ +# 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-pdf-toolbar", + ] +} + +js_library("viewer-pdf-toolbar") { + deps = [ + "../viewer-bookmark:viewer-bookmark", + "../viewer-page-selector:viewer-page-selector", + "../viewer-toolbar-dropdown:viewer-toolbar-dropdown", + ] + externs_list = [ "$externs_path/pending.js" ] +} 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 f6454c97a5c..e183debc86d 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 @@ -5,7 +5,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="../icons.html"> -<link rel="import" href="../viewer-bookmarks-content/viewer-bookmarks-content.html"> +<link rel="import" href="../viewer-bookmark/viewer-bookmark.html"> <link rel="import" href="../viewer-page-selector/viewer-page-selector.html"> <if expr="chromeos"> <link rel="import" href="../viewer-pen-options/viewer-pen-options.html"> @@ -214,8 +214,9 @@ open-icon="pdf:bookmark" closed-icon="pdf:bookmark-border" header="{{strings.bookmarks}}"> - <viewer-bookmarks-content bookmarks="{{bookmarks}}"> - </viewer-bookmarks-content> + <template is="dom-repeat" items="[[bookmarks]]"> + <viewer-bookmark bookmark="[[item]]" depth="0"></viewer-bookmark> + </template> </viewer-toolbar-dropdown> </div> </div> @@ -228,7 +229,7 @@ <div id="annotations-bar" hidden> <viewer-toolbar-dropdown id="pen" - selected$="[[equal_('pen', annotationTool.tool)]]" + selected$="[[isAnnotationTool_('pen', annotationTool.tool)]]" open-after-select on-click="annotationToolClicked_" open-icon="pdf:marker" @@ -247,7 +248,7 @@ </viewer-toolbar-dropdown> <viewer-toolbar-dropdown id="highlighter" - selected$="[[equal_('highlighter', annotationTool.tool)]]" + selected$="[[isAnnotationTool_('highlighter', annotationTool.tool)]]" open-after-select on-click="annotationToolClicked_" open-icon="pdf:highlighter" @@ -266,7 +267,7 @@ </viewer-toolbar-dropdown> <cr-icon-button id="eraser" - selected$="[[equal_('eraser', annotationTool.tool)]]" + selected$="[[isAnnotationTool_('eraser', annotationTool.tool)]]" on-click="annotationToolClicked_" iron-icon="pdf:eraser" aria-label$="{{strings.annotationEraser}}" title$="{{strings.annotationEraser}}"></cr-icon-button> 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 006aa5ca5b8..afb87dfe6b7 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 @@ -7,38 +7,16 @@ Polymer({ properties: { /** - * The current loading progress of the PDF document (0 - 100). - */ - loadProgress: {type: Number, observer: 'loadProgressChanged_'}, - - /** - * The title of the PDF document. - */ - docTitle: String, - - /** - * The number of the page being viewed (1-based). - */ - pageNo: Number, - - /** - * Tree of PDF bookmarks (or null if the document has no bookmarks). - */ - bookmarks: {type: Object, value: null}, - - /** - * The number of pages in the PDF document. - */ - docLength: Number, - - /** - * Whether the toolbar is opened and visible. + * 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. */ - opened: {type: Boolean, value: true}, + annotationAvailable: { + type: Boolean, + value: true, + }, - /** - * Whether the viewer is currently in annotation mode. - */ + /** Whether the viewer is currently in annotation mode. */ annotationMode: { type: Boolean, notify: true, @@ -46,6 +24,7 @@ Polymer({ reflectToAttribute: true, }, + /** @type {?Object} */ annotationTool: { type: Object, value: null, @@ -53,13 +32,17 @@ 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. + * Tree of PDF bookmarks (empty if the document has no bookmarks). + * @type {!Array<!Bookmark>} */ - annotationAvailable: { + bookmarks: { + type: Array, + value: () => [], + }, + + canRedoAnnotation: { type: Boolean, - value: true, + value: false, }, canUndoAnnotation: { @@ -67,22 +50,34 @@ Polymer({ value: false, }, - canRedoAnnotation: { + /** The number of pages in the PDF document. */ + docLength: Number, + + /** The title of the PDF document. */ + docTitle: String, + + /** The current loading progress of the PDF document (0 - 100). */ + loadProgress: { + type: Number, + observer: 'loadProgressChanged_', + }, + + /** Whether the toolbar is opened and visible. */ + opened: { type: Boolean, - value: false, + value: true, }, - /** - * Whether the PDF Annotations feature is enabled. - */ + /** The number of the page being viewed (1-based). */ + pageNo: Number, + + /** Whether the PDF Annotations feature is enabled. */ pdfAnnotationsEnabled: { type: Boolean, value: false, }, - /** - * Whether the Printing feature is enabled. - */ + /** Whether the Printing feature is enabled. */ printingEnabled: { type: Boolean, value: false, @@ -91,6 +86,9 @@ Polymer({ strings: Object, }, + /** @type {?Object} */ + animation_: null, + /** * @param {number} newProgress * @param {number} oldProgress @@ -130,22 +128,16 @@ Polymer({ if (this.opened) { this.animation_ = this.animate( - { - transform: ['translateY(-100%)', 'translateY(0%)'], - }, - { - easing: 'cubic-bezier(0, 0, 0.2, 1)', + [{transform: 'translateY(-100%)'}, {transform: 'translateY(0%)'}], { duration: 250, + easing: 'cubic-bezier(0, 0, 0.2, 1)', fill: 'forwards', }); } else { this.animation_ = this.animate( - { - transform: ['translateY(0%)', 'translateY(-100%)'], - }, - { - easing: 'cubic-bezier(0.4, 0, 1, 1)', + [{transform: 'translateY(0%)'}, {transform: 'translateY(-100%)'}], { duration: 250, + easing: 'cubic-bezier(0.4, 0, 1, 1)', fill: 'forwards', }); } @@ -155,11 +147,13 @@ Polymer({ this.$.pageselector.select(); }, + /** @return {boolean} Whether the toolbar should be kept open. */ shouldKeepOpen: function() { return this.$.bookmarks.dropdownOpen || this.loadProgress < 100 || this.$.pageselector.isActive() || this.annotationMode; }, + /** @return {boolean} Whether a dropdown was open and was hidden. */ hideDropdowns: function() { let result = false; if (this.$.bookmarks.dropdownOpen) { @@ -177,6 +171,7 @@ Polymer({ return result; }, + /** @param {number} lowerBound */ setDropdownLowerBound: function(lowerBound) { this.$.bookmarks.lowerBound = lowerBound; }, @@ -214,12 +209,18 @@ Polymer({ })); }, - /** @param {Event} e */ + /** + * @param {!Event} e + * @private + */ annotationToolClicked_: function(e) { - this.updateAnnotationTool_(e.currentTarget); + this.updateAnnotationTool_(/** @type {!HTMLElement} */ (e.currentTarget)); }, - /** @param {Event} e */ + /** + * @param {!Event} e + * @private + */ annotationToolOptionChanged_: function(e) { const element = e.currentTarget.parentElement; if (!this.annotationTool || element.id != this.annotationTool.tool) { @@ -228,15 +229,19 @@ Polymer({ this.updateAnnotationTool_(e.currentTarget.parentElement); }, - /** @param {Element} element */ + /** + * @param {!HTMLElement} element + * @private + */ updateAnnotationTool_: function(element) { const tool = element.id; const options = element.querySelector('viewer-pen-options') || { selectedSize: 1, selectedColor: null, }; - element.attributeStyleMap.set('--pen-tip-fill', options.selectedColor); - element.attributeStyleMap.set( + const attributeStyleMap = element.attributeStyleMap; + attributeStyleMap.set('--pen-tip-fill', options.selectedColor); + attributeStyleMap.set( '--pen-tip-border', options.selectedColor == '#000000' ? 'currentcolor' : options.selectedColor); @@ -248,14 +253,12 @@ Polymer({ }, /** - * Used to determine equality in computed bindings. - * - * @param {*} a - * @param {*} b + * @param {string} toolName + * @return {boolean} Whether the annotation tool is using tool |toolName|. + * @private */ - equal_: function(a, b) { - return a == b; + isAnnotationTool_: function(toolName) { + return !!this.annotationTool && this.annotationTool.tool === toolName; }, - }); })(); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/BUILD.gn new file mode 100644 index 00000000000..7a2ca5ce91a --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/BUILD.gn @@ -0,0 +1,15 @@ +# 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-toolbar-dropdown", + ] +} + +js_library("viewer-toolbar-dropdown") { + deps = [] +} 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 c1964a19a20..1ebf0df604f 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 @@ -91,7 +91,7 @@ } </style> <cr-icon-button on-click="toggleDropdown" id="button" - iron-icon="[[dropdownIcon]],cr:arrow-drop-down" title$="[[header]]"> + iron-icon="[[dropdownIcon_]],cr:arrow-drop-down" title$="[[header]]"> </cr-icon-button> <div id="container"> 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 05f044afe47..00b2b676b45 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 @@ -18,6 +18,23 @@ Polymer({ is: 'viewer-toolbar-dropdown', properties: { + /** Icon to display when the dropdown is closed. */ + closedIcon: String, + + /** Whether the dropdown should be centered or right aligned. */ + dropdownCentered: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + + /** True if the dropdown is currently open. */ + dropdownOpen: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + /** * String to be displayed at the top of the dropdown and for the tooltip * of the button. @@ -25,43 +42,53 @@ Polymer({ header: String, /** Whether to hide the header at the top of the dropdown. */ - hideHeader: {type: Boolean, value: false}, - - /** Icon to display when the dropdown is closed. */ - closedIcon: String, + hideHeader: { + type: Boolean, + value: false, + }, - /** Icon to display when the dropdown is open. */ - openIcon: String, + /** Lowest vertical point that the dropdown should occupy (px). */ + lowerBound: { + type: Number, + observer: 'lowerBoundChanged_', + }, /** Unique id to identify this dropdown for metrics purposes. */ metricsId: String, - /** True if the dropdown is currently open. */ - dropdownOpen: {type: Boolean, reflectToAttribute: true, value: false}, + /** Whether the dropdown must be selected before opening. */ + openAfterSelect: { + type: Boolean, + value: false, + }, - /** Whether the dropdown should be centered or right aligned. */ - dropdownCentered: {type: Boolean, reflectToAttribute: true, value: false}, + /** Icon to display when the dropdown is open. */ + openIcon: String, /** Whether the dropdown is marked as selected. */ - selected: {type: Boolean, reflectToAttribute: true, value: false}, - - /** Whether the dropdown must be selected before opening. */ - openAfterSelect: {type: Boolean, reflectToAttribute: true, value: false}, + selected: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, - /** Toolbar icon currently being displayed. */ - dropdownIcon: { + /** + * Toolbar icon currently being displayed. + * @private + */ + dropdownIcon_: { type: String, - computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)' + computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)', }, - - /** Lowest vertical point that the dropdown should occupy (px). */ - lowerBound: {type: Number, observer: 'lowerBoundChanged_'}, - - /** Current animation being played, or null if there is none. */ - animation_: Object }, /** + * Current animation being played, or null if there is none. + * @private {?Object} + */ + animation_: null, + + /** * True if the max-height CSS property for the dropdown scroll container * is valid. If false, the height will be updated the next time the * dropdown is visible. @@ -69,10 +96,15 @@ Polymer({ */ maxHeightValid_: false, + /** + * @return {string} Current icon for the dropdown. + * @private + */ computeIcon_: function(dropdownOpen, closedIcon, openIcon) { return dropdownOpen ? openIcon : closedIcon; }, + /** @private */ lowerBoundChanged_: function() { this.maxHeightValid_ = false; if (this.dropdownOpen) { @@ -137,6 +169,10 @@ Polymer({ }; }, + /** + * @return {!Object} Animation + * @private + */ animateEntry_: function() { let maxHeight = this.$.dropdown.getBoundingClientRect().height - DROPDOWN_OUTER_PADDING; @@ -164,6 +200,10 @@ Polymer({ }); }, + /** + * @return {!Object} Animation + * @private + */ animateExit_: function() { return this.$.dropdown.animate( [ diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/BUILD.gn new file mode 100644 index 00000000000..4c75971c261 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/BUILD.gn @@ -0,0 +1,25 @@ +# 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-zoom-button", + ":viewer-zoom-toolbar", + ] +} + +js_library("viewer-zoom-toolbar") { + deps = [ + ":viewer-zoom-button", + "../..:pdf_fitting_type", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:util", + ] +} + +js_library("viewer-zoom-button") { + deps = [] +} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html index a3ae841c93f..000d098cfb3 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html @@ -19,14 +19,14 @@ transition-timing-function: cubic-bezier(0, 0, 0.2, 1); } - :host([closed]) #wrapper { + :host([closed_]) #wrapper { /* 132px roughly flips the location of the button across the right edge * of the page. */ transform: translateX(var(--translate-x-distance)); transition-timing-function: cubic-bezier(0.4, 0, 1, 1); } - :host([show-on-left][closed]) #wrapper { + :host([show-on-left][closed_]) #wrapper { transform: translateX(calc(-1 * var(--translate-x-distance))); } @@ -76,7 +76,7 @@ } </style> <div id="wrapper"> - <cr-icon-button iron-icon="[[visibleIcon_]]" on-click="fireClick" + <cr-icon-button iron-icon="[[visibleIcon_]]" on-click="fireClick_" aria-label$="[[visibleTooltip_]]" title="[[visibleTooltip_]]"> </cr-icon-button> </div> diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js index 361b292e81f..2359900d83c 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js @@ -6,6 +6,17 @@ Polymer({ is: 'viewer-zoom-button', properties: { + /** Index of the icon currently being displayed. */ + activeIndex: { + type: Number, + value: 0, + }, + + delay: { + type: Number, + observer: 'delayChanged_', + }, + /** * Icons to be displayed on the FAB. Multiple icons should be separated with * spaces, and will be cycled through every time the FAB is clicked. @@ -13,14 +24,6 @@ Polymer({ icons: String, /** - * Array version of the list of icons. Polymer does not allow array - * properties to be set from HTML, so we must use a string property and - * perform the conversion manually. - * @private - */ - icons_: {type: Array, value: [''], computed: 'computeIconsArray_(icons)'}, - - /** * Used to show the appropriate drop shadow when buttons are focused with * the keyboard. */ @@ -29,59 +32,98 @@ Polymer({ reflectToAttribute: true, }, - tooltips: Array, - - closed: {type: Boolean, reflectToAttribute: true, value: false}, + newPrintPreview: { + type: Boolean, + reflectToAttribute: true, + }, - delay: {type: Number, observer: 'delayChanged_'}, + showOnLeft: { + type: Boolean, + reflectToAttribute: true, + }, - newPrintPreview: {type: Boolean, reflectToAttribute: true}, + /** @type {?Array<string>} */ + tooltips: Array, - showOnLeft: {type: Boolean, reflectToAttribute: true}, + /** @private */ + closed_: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, /** - * Index of the icon currently being displayed. + * Array version of the list of icons. Polymer does not allow array + * properties to be set from HTML, so we must use a string property and + * perform the conversion manually. + * @private {!Array<string>} */ - activeIndex: {type: Number, value: 0}, + icons_: { + type: Array, + value: [''], + computed: 'computeIconsArray_(icons)', + }, /** * Icon currently being displayed on the FAB. * @private */ - visibleIcon_: - {type: String, computed: 'computeVisibleIcon_(icons_, activeIndex)'}, + visibleIcon_: { + type: String, + computed: 'computeVisibleIcon_(icons_, activeIndex)', + }, + /** @private */ visibleTooltip_: { type: String, - computed: 'computeVisibleTooltip_(tooltips, activeIndex)' + computed: 'computeVisibleTooltip_(tooltips, activeIndex)', } }, + /** + * @param {string} icons Icon names in a string, delimited by spaces + * @return {!Array<string>} Array of icon name strings + * @private + */ computeIconsArray_: function(icons) { return icons.split(' '); }, + /** + * @param {!Array<string>} icons Array of icon name strings. + * @param {number} activeIndex Index of the currently active icon. + * @return {string} Icon name for the currently visible icon. + * @private + */ computeVisibleIcon_: function(icons, activeIndex) { return icons[activeIndex]; }, + /** + * @param {?Array<string>} tooltips Array of tooltip strings. + * @param {number} activeIndex Index of the currently active icon. + * @return {string} Tooltip for the currently visible icon. + * @private + */ computeVisibleTooltip_: function(tooltips, activeIndex) { return tooltips === undefined ? '' : tooltips[activeIndex]; }, + /** @private */ delayChanged_: function() { this.$.wrapper.style.transitionDelay = this.delay + 'ms'; }, show: function() { - this.closed = false; + this.closed_ = false; }, hide: function() { - this.closed = true; + this.closed_ = true; }, - fireClick: function() { + /** @private */ + fireClick_: function() { // We cannot attach an on-click to the entire viewer-zoom-button, as this // will include clicks on the margins. Instead, proxy clicks on the FAB // through. diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js index fb817a5a01d..fe24ab64645 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js @@ -52,6 +52,7 @@ Polymer({ return this.isPrintPreview_; }, + /** @return {boolean} */ isVisible: function() { return this.visible_; }, @@ -94,7 +95,10 @@ Polymer({ /** * Change button tooltips to match any changes to localized strings. - * @param {!Object} strings + * @param {!{tooltipFitToPage: string, + * tooltipFitToWidth: string, + * tooltipZoomIn: string, + * tooltipZoomOut: string}} strings */ setStrings: function(strings) { this.$['fit-button'].tooltips = @@ -103,9 +107,7 @@ Polymer({ this.$['zoom-out-button'].tooltips = [strings.tooltipZoomOut]; }, - /** - * Handle clicks of the fit-button. - */ + /** Handle clicks of the fit-button. */ fitToggle: function() { this.fireFitToChangedEvent_( this.$['fit-button'].activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? @@ -114,9 +116,7 @@ Polymer({ true); }, - /** - * Handle the keyboard shortcut equivalent of fit-button clicks. - */ + /** Handle the keyboard shortcut equivalent of fit-button clicks. */ fitToggleFromHotKey: function() { this.fitToggle(); @@ -130,7 +130,7 @@ Polymer({ /** * Handle forcing zoom via scripting to a fitting type. - * @param {FittingType} fittingType Page fitting type to force. + * @param {!FittingType} fittingType Page fitting type to force. */ forceFit: function(fittingType) { this.fireFitToChangedEvent_(fittingType, false); @@ -143,11 +143,11 @@ Polymer({ }, /** - * @private * Fire a 'fit-to-changed' {CustomEvent} with the given FittingType as detail. - * @param {FittingType} fittingType to include as payload. + * @param {!FittingType} fittingType to include as payload. * @param {boolean} userInitiated whether the event was initiated by a user * action. + * @private */ fireFitToChangedEvent_: function(fittingType, userInitiated) { this.fire( diff --git a/chromium/chrome/browser/resources/pdf/index.html b/chromium/chrome/browser/resources/pdf/index.html index c1c362756b3..06d219a6c7e 100644 --- a/chromium/chrome/browser/resources/pdf/index.html +++ b/chromium/chrome/browser/resources/pdf/index.html @@ -12,6 +12,8 @@ <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"> + <link rel="import" href="chrome://resources/html/cr/event_target.html"> + <link rel="import" href="chrome://resources/html/event_tracker.html"> <if expr="chromeos"> <link rel="import" href="elements/viewer-ink-host/viewer-ink-host.html"> @@ -43,7 +45,6 @@ </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/navigator.js b/chromium/chrome/browser/resources/pdf/navigator.js index 0282e725de2..d2e21dde43e 100644 --- a/chromium/chrome/browser/resources/pdf/navigator.js +++ b/chromium/chrome/browser/resources/pdf/navigator.js @@ -5,42 +5,24 @@ 'use strict'; /** - * Creates a new NavigatorDelegate for calling browser-specific functions to - * do the actual navigating. - * - * @param {number} tabId The tab ID of the PDF viewer or -1 if the viewer is - * not displayed in a tab. - * @constructor + * NavigatorDelegate for calling browser-specific functions to do the actual + * navigating. */ -function NavigatorDelegate(tabId) { - this.tabId_ = tabId; -} - -/** - * Creates a new Navigator for navigating to links inside or outside the PDF. - * - * @param {string} originalUrl The original page URL. - * @param {Object} viewport The viewport info of the page. - * @param {Object} paramsParser The object for URL parsing. - * @param {Object} navigatorDelegate The object with callback functions that - * get called when navigation happens in the current tab, a new tab, - * and a new window. - * @constructor - */ -function Navigator(originalUrl, viewport, paramsParser, navigatorDelegate) { - this.originalUrl_ = originalUrl; - this.viewport_ = viewport; - this.paramsParser_ = paramsParser; - this.navigatorDelegate_ = navigatorDelegate; -} +class NavigatorDelegate { + /** + * @param {number} tabId The tab ID of the PDF viewer or -1 if the viewer is + * not displayed in a tab. + */ + constructor(tabId) { + /** @private {number} */ + this.tabId_ = tabId; + } -NavigatorDelegate.prototype = { /** * Called when navigation should happen in the current tab. - * * @param {string} url The url to be opened in the current tab. */ - navigateInCurrentTab: function(url) { + navigateInCurrentTab(url) { // When the PDFviewer is inside a browser tab, prefer the tabs API because // it can navigate from one file:// URL to another. if (chrome.tabs && this.tabId_ != -1) { @@ -48,15 +30,14 @@ NavigatorDelegate.prototype = { } else { window.location.href = url; } - }, + } /** * Called when navigation should happen in the new tab. - * * @param {string} url The url to be opened in the new tab. * @param {boolean} active Indicates if the new tab should be the active tab. */ - navigateInNewTab: function(url, active) { + navigateInNewTab(url, active) { // Prefer the tabs API because it guarantees we can just open a new tab. // window.open doesn't have this guarantee. if (chrome.tabs) { @@ -64,14 +45,13 @@ NavigatorDelegate.prototype = { } else { window.open(url); } - }, + } /** * Called when navigation should happen in the new window. - * * @param {string} url The url to be opened in the new window. */ - navigateInNewWindow: function(url) { + navigateInNewWindow(url) { // Prefer the windows API because it guarantees we can just open a new // window. window.open with '_blank' argument doesn't have this guarantee. if (chrome.windows) { @@ -80,52 +60,69 @@ NavigatorDelegate.prototype = { window.open(url, '_blank'); } } -}; +} -/** - * Represents options when navigating to a new url. C++ counterpart of - * the enum is in ui/base/window_open_disposition.h. This enum represents - * the only values that are passed from Plugin. - * @enum {number} - */ -Navigator.WindowOpenDisposition = { - CURRENT_TAB: 1, - NEW_FOREGROUND_TAB: 3, - NEW_BACKGROUND_TAB: 4, - NEW_WINDOW: 6, - SAVE_TO_DISK: 7 -}; +/** Navigator for navigating to links inside or outside the PDF. */ +class PdfNavigator { + /** + * @param {string} originalUrl The original page URL. + * @param {!Viewport} viewport The viewport info of the page. + * @param {!OpenPdfParamsParser} paramsParser The object for URL parsing. + * @param {!NavigatorDelegate} navigatorDelegate The object with callback + * functions that get called when navigation happens in the current tab, + * a new tab, and a new window. + */ + constructor(originalUrl, viewport, paramsParser, navigatorDelegate) { + /** @private {?URL} */ + this.originalUrl_ = null; + try { + this.originalUrl_ = new URL(originalUrl); + } catch (err) { + console.warn('Invalid original URL'); + } + + /** @private {!Viewport} */ + this.viewport_ = viewport; + + /** @private {!OpenPdfParamsParser} */ + this.paramsParser_ = paramsParser; + + /** @private {!NavigatorDelegate} */ + this.navigatorDelegate_ = navigatorDelegate; + } -Navigator.prototype = { /** * Function to navigate to the given URL. This might involve navigating * within the PDF page or opening a new url (in the same tab or a new tab). - * - * @param {string} url The URL to navigate to. - * @param {number} disposition The window open disposition when - * navigating to the new URL. + * @param {string} urlString The URL to navigate to. + * @param {!PdfNavigator.WindowOpenDisposition} disposition The window open + * disposition when navigating to the new URL. */ - navigate: function(url, disposition) { - if (url.length == 0) { + navigate(urlString, disposition) { + if (urlString.length == 0) { return; } // If |urlFragment| starts with '#', then it's for the same URL with a // different URL fragment. - if (url.charAt(0) == '#') { + if (urlString[0] === '#' && this.originalUrl_) { // if '#' is already present in |originalUrl| then remove old fragment // and add new url fragment. - const hashIndex = this.originalUrl_.search('#'); - if (hashIndex != -1) { - url = this.originalUrl_.substring(0, hashIndex) + url; - } else { - url = this.originalUrl_ + url; - } + const newUrl = new URL(this.originalUrl_.href); + newUrl.hash = urlString; + urlString = newUrl.href; } // If there's no scheme, then take a guess at the scheme. - if (url.indexOf('://') == -1 && url.indexOf('mailto:') == -1) { - url = this.guessUrlWithoutScheme_(url); + if (!urlString.includes('://') && !urlString.includes('mailto:')) { + urlString = this.guessUrlWithoutScheme_(urlString); + } + + let url = null; + try { + url = new URL(urlString); + } catch (err) { + return; } if (!this.isValidUrl_(url)) { @@ -133,147 +130,134 @@ Navigator.prototype = { } switch (disposition) { - case Navigator.WindowOpenDisposition.CURRENT_TAB: + case PdfNavigator.WindowOpenDisposition.CURRENT_TAB: this.paramsParser_.getViewportFromUrlParams( - url, this.onViewportReceived_.bind(this)); + url.href, this.onViewportReceived_.bind(this)); break; - case Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB: - this.navigatorDelegate_.navigateInNewTab(url, false); + case PdfNavigator.WindowOpenDisposition.NEW_BACKGROUND_TAB: + this.navigatorDelegate_.navigateInNewTab(url.href, false); break; - case Navigator.WindowOpenDisposition.NEW_FOREGROUND_TAB: - this.navigatorDelegate_.navigateInNewTab(url, true); + case PdfNavigator.WindowOpenDisposition.NEW_FOREGROUND_TAB: + this.navigatorDelegate_.navigateInNewTab(url.href, true); break; - case Navigator.WindowOpenDisposition.NEW_WINDOW: - this.navigatorDelegate_.navigateInNewWindow(url); + case PdfNavigator.WindowOpenDisposition.NEW_WINDOW: + this.navigatorDelegate_.navigateInNewWindow(url.href); break; - case Navigator.WindowOpenDisposition.SAVE_TO_DISK: + case PdfNavigator.WindowOpenDisposition.SAVE_TO_DISK: // TODO(jaepark): Alt + left clicking a link in PDF should // download the link. this.paramsParser_.getViewportFromUrlParams( - url, this.onViewportReceived_.bind(this)); + url.href, this.onViewportReceived_.bind(this)); break; default: break; } - }, + } /** * Called when the viewport position is received. - * * @param {Object} viewportPosition Dictionary containing the viewport * position. * @private */ - onViewportReceived_: function(viewportPosition) { - let originalUrl = this.originalUrl_; - let hashIndex = originalUrl.search('#'); - if (hashIndex != -1) { - originalUrl = originalUrl.substring(0, hashIndex); - } - - let newUrl = viewportPosition.url; - hashIndex = newUrl.search('#'); - if (hashIndex != -1) { - newUrl = newUrl.substring(0, hashIndex); + onViewportReceived_(viewportPosition) { + let newUrl = null; + try { + newUrl = new URL(viewportPosition.url); + } catch (err) { } const pageNumber = viewportPosition.page; - if (pageNumber != undefined && originalUrl == newUrl) { + if (pageNumber != undefined && this.originalUrl_ && newUrl && + this.originalUrl_.origin === newUrl.origin && + this.originalUrl_.pathname === newUrl.pathname) { this.viewport_.goToPage(pageNumber); } else { this.navigatorDelegate_.navigateInCurrentTab(viewportPosition.url); } - }, + } /** * Checks if the URL starts with a scheme and is not just a scheme. - * - * @param {string} url The input URL + * @param {!URL} url The input URL * @return {boolean} Whether the url is valid. * @private */ - isValidUrl_: function(url) { + isValidUrl_(url) { // Make sure |url| starts with a valid scheme. - if (!url.startsWith('http://') && !url.startsWith('https://') && - !url.startsWith('ftp://') && !url.startsWith('file://') && - !url.startsWith('mailto:')) { + const validSchemes = ['http:', 'https:', 'ftp:', 'file:', 'mailto:']; + if (!validSchemes.includes(url.protocol)) { return false; } // Navigations to file:-URLs are only allowed from file:-URLs. - if (url.startsWith('file:') && !this.originalUrl_.startsWith('file:')) { - return false; - } - - - // Make sure |url| is not only a scheme. - if (url == 'http://' || url == 'https://' || url == 'ftp://' || - url == 'file://' || url == 'mailto:') { + if (url.protocol === 'file:' && this.originalUrl_ && + this.originalUrl_.protocol !== 'file:') { return false; } return true; - }, + } /** * Attempt to figure out what a URL is when there is no scheme. - * * @param {string} url The input URL * @return {string} The URL with a scheme or the original URL if it is not * possible to determine the scheme. * @private */ - guessUrlWithoutScheme_: function(url) { + guessUrlWithoutScheme_(url) { // If the original URL is mailto:, that does not make sense to start with, // and neither does adding |url| to it. // If the original URL is not a valid URL, this cannot make a valid URL. // In both cases, just bail out. - if (this.originalUrl_.startsWith('mailto:') || + if (!this.originalUrl_ || this.originalUrl_.protocol === 'mailto:' || !this.isValidUrl_(this.originalUrl_)) { return url; } // Check for absolute paths. if (url.startsWith('/')) { - const schemeEndIndex = this.originalUrl_.indexOf('://'); - const firstSlash = this.originalUrl_.indexOf('/', schemeEndIndex + 3); - // e.g. http://www.foo.com/bar -> http://www.foo.com - const domain = firstSlash != -1 ? - this.originalUrl_.substr(0, firstSlash) : - this.originalUrl_; - return domain + url; - } - - // Check for obvious relative paths. - let isRelative = false; - if (url.startsWith('.') || url.startsWith('\\')) { - isRelative = true; + return this.originalUrl_.origin + url; } + // Check for other non-relative paths. // In Adobe Acrobat Reader XI, it looks as though links with less than // 2 dot separators in the domain are considered relative links, and - // those with 2 of more are considered http URLs. e.g. + // those with 2 or more are considered http URLs. e.g. // // www.foo.com/bar -> http // foo.com/bar -> relative link - if (!isRelative) { + if (url.startsWith('\\')) { + // Prepend so that the relative URL will be correctly computed by new + // URL() below. + url = './' + url; + } + if (!url.startsWith('.')) { const domainSeparatorIndex = url.indexOf('/'); const domainName = domainSeparatorIndex == -1 ? url : url.substr(0, domainSeparatorIndex); const domainDotCount = (domainName.match(/\./g) || []).length; - if (domainDotCount < 2) { - isRelative = true; + if (domainDotCount >= 2) { + return 'http://' + url; } } - if (isRelative) { - const slashIndex = this.originalUrl_.lastIndexOf('/'); - const path = slashIndex != -1 ? this.originalUrl_.substr(0, slashIndex) : - this.originalUrl_; - return path + '/' + url; - } - - return 'http://' + url; + return new URL(url, this.originalUrl_.href).href; } +} + +/** + * Represents options when navigating to a new url. C++ counterpart of + * the enum is in ui/base/window_open_disposition.h. This enum represents + * the only values that are passed from Plugin. + * @enum {number} + */ +PdfNavigator.WindowOpenDisposition = { + CURRENT_TAB: 1, + NEW_FOREGROUND_TAB: 3, + NEW_BACKGROUND_TAB: 4, + NEW_WINDOW: 6, + SAVE_TO_DISK: 7 }; diff --git a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js index aacb7c437ec..d4a502a135f 100644 --- a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js +++ b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js @@ -2,15 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -(function() { - 'use strict'; /** * Parses the open pdf parameters passed in the url to set initial viewport * settings for opening the pdf. */ -window.OpenPDFParamsParser = class { +class OpenPdfParamsParser { /** * @param {function(Object)} postMessageCallback * Function called to fetch information for a named destination. @@ -206,6 +204,4 @@ window.OpenPDFParamsParser = class { } outstandingRequest.callback(outstandingRequest.params); } -}; - -}()); +} diff --git a/chromium/chrome/browser/resources/pdf/pdf_viewer.js b/chromium/chrome/browser/resources/pdf/pdf_viewer.js index 02763ae00c9..375663f029c 100644 --- a/chromium/chrome/browser/resources/pdf/pdf_viewer.js +++ b/chromium/chrome/browser/resources/pdf/pdf_viewer.js @@ -151,7 +151,7 @@ function PDFViewer(browserApi) { PDFMetrics.record(PDFMetrics.UserAction.DOCUMENT_OPENED); // Parse open pdf parameters. - this.paramsParser_ = new OpenPDFParamsParser( + this.paramsParser_ = new OpenPdfParamsParser( message => this.pluginController_.postMessage(message)); const toolbarEnabled = this.paramsParser_.getUiUrlParams(this.originalUrl_).toolbar && @@ -161,12 +161,13 @@ function PDFViewer(browserApi) { // to be displayed in the window. It is sized according to the document size // of the pdf and zoom level. this.sizer_ = $('sizer'); + if (this.isPrintPreview_) { this.pageIndicator_ = $('page-indicator'); } this.passwordScreen_ = $('password-screen'); this.passwordScreen_.addEventListener( - 'password-submitted', this.onPasswordSubmitted_.bind(this)); + 'password-submitted', e => this.onPasswordSubmitted_(e)); this.errorScreen_ = $('error-screen'); // Can only reload if we are in a normal tab. if (chrome.tabs && this.browserApi_.getStreamInfo().tabId != -1) { @@ -183,15 +184,16 @@ function PDFViewer(browserApi) { this.browserApi_.getZoomBehavior() == BrowserApi.ZoomBehavior.MANAGE ? this.browserApi_.getDefaultZoom() : 1.0; - this.viewport_ = new ViewportImpl( - window, this.sizer_, this.viewportChanged_.bind(this), - () => this.currentController_.beforeZoom(), - () => { - this.currentController_.afterZoom(); - this.zoomManager_.onPdfZoomChange(); - }, - this.setUserInitiated_.bind(this), getScrollbarWidth(), defaultZoom, - topToolbarHeight); + this.viewport_ = new Viewport( + window, this.sizer_, getScrollbarWidth(), defaultZoom, topToolbarHeight); + this.viewport_.setViewportChangedCallback(() => this.viewportChanged_()); + this.viewport_.setBeforeZoomCallback( + () => this.currentController_.beforeZoom()); + this.viewport_.setAfterZoomCallback( + () => this.currentController_.afterZoom()); + this.viewport_.setUserInitiatedCallback( + userInitiated => this.setUserInitiated_(userInitiated)); + window.addEventListener('beforeunload', () => this.viewport_.resetTracker()); // 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 @@ -208,7 +210,7 @@ function PDFViewer(browserApi) { // 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); + 'message', message => this.handleScriptingMessage(message), false); this.plugin_.setAttribute('src', this.originalUrl_); this.plugin_.setAttribute( @@ -242,20 +244,18 @@ function PDFViewer(browserApi) { this.zoomToolbar_ = $('zoom-toolbar'); this.zoomToolbar_.setIsPrintPreview(this.isPrintPreview_); this.zoomToolbar_.addEventListener( - 'fit-to-changed', this.fitToChanged_.bind(this)); - this.zoomToolbar_.addEventListener( - 'zoom-in', this.viewport_.zoomIn.bind(this.viewport_)); + 'fit-to-changed', e => this.fitToChanged_(e)); + this.zoomToolbar_.addEventListener('zoom-in', () => this.viewport_.zoomIn()); this.zoomToolbar_.addEventListener( - 'zoom-out', this.viewport_.zoomOut.bind(this.viewport_)); + 'zoom-out', () => this.viewport_.zoomOut()); this.gestureDetector_ = new GestureDetector($('content')); this.gestureDetector_.addEventListener( - 'pinchstart', this.onPinchStart_.bind(this)); + 'pinchstart', e => this.onPinchStart_(e)); this.sentPinchEvent_ = false; this.gestureDetector_.addEventListener( - 'pinchupdate', this.onPinchUpdate_.bind(this)); - this.gestureDetector_.addEventListener( - 'pinchend', this.onPinchEnd_.bind(this)); + 'pinchupdate', e => this.onPinchUpdate_(e)); + this.gestureDetector_.addEventListener('pinchend', e => this.onPinchEnd_(e)); if (toolbarEnabled) { this.toolbar_ = $('toolbar'); @@ -293,8 +293,8 @@ function PDFViewer(browserApi) { document.body.addEventListener('navigate', e => { const disposition = e.detail.newtab ? - Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB : - Navigator.WindowOpenDisposition.CURRENT_TAB; + PdfNavigator.WindowOpenDisposition.NEW_BACKGROUND_TAB : + PdfNavigator.WindowOpenDisposition.CURRENT_TAB; this.navigator_.navigate(e.detail.uri, disposition); }); @@ -309,33 +309,34 @@ function PDFViewer(browserApi) { // Set up the ZoomManager. this.zoomManager_ = ZoomManager.create( - this.browserApi_.getZoomBehavior(), this.viewport_, - this.browserApi_.setZoom.bind(this.browserApi_), + this.browserApi_.getZoomBehavior(), () => this.viewport_.getZoom(), + zoom => this.browserApi_.setZoom(zoom), this.browserApi_.getInitialZoom()); - this.viewport_.zoomManager = this.zoomManager_; + this.viewport_.setZoomManager(this.zoomManager_); this.browserApi_.addZoomEventListener( - this.zoomManager_.onBrowserZoomChange.bind(this.zoomManager_)); + zoom => this.zoomManager_.onBrowserZoomChange(zoom)); // Setup the keyboard event listener. - document.addEventListener('keydown', this.handleKeyEvent_.bind(this)); - document.addEventListener('mousemove', this.handleMouseEvent_.bind(this)); - document.addEventListener('mouseout', this.handleMouseEvent_.bind(this)); + document.addEventListener('keydown', e => this.handleKeyEvent_(e)); + document.addEventListener('mousemove', e => this.handleMouseEvent_(e)); + document.addEventListener('mouseout', e => this.handleMouseEvent_(e)); document.addEventListener( - 'contextmenu', this.handleContextMenuEvent_.bind(this)); + 'contextmenu', e => this.handleContextMenuEvent_(e)); const tabId = this.browserApi_.getStreamInfo().tabId; - this.navigator_ = new Navigator( + this.navigator_ = new PdfNavigator( this.originalUrl_, this.viewport_, this.paramsParser_, new NavigatorDelegate(tabId)); this.viewportScroller_ = new ViewportScroller(this.viewport_, this.plugin_, window); // Request translated strings. - chrome.resourcesPrivate.getStrings('pdf', this.handleStrings_.bind(this)); + chrome.resourcesPrivate.getStrings( + 'pdf', strings => this.handleStrings_(strings)); // Listen for save commands from the browser. if (chrome.mimeHandlerPrivate && chrome.mimeHandlerPrivate.onSave) { - chrome.mimeHandlerPrivate.onSave.addListener(this.onSave.bind(this)); + chrome.mimeHandlerPrivate.onSave.addListener(url => this.onSave(url)); } } @@ -361,7 +362,7 @@ PDFViewer.prototype = { const pageUpHandler = () => { // Go to the previous page if we are fit-to-page or fit-to-height. if (this.viewport_.isPagedMode()) { - this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1); + this.viewport_.goToPreviousPage(); // Since we do the movement of the page. e.preventDefault(); } else if (fromScriptingAPI) { @@ -372,7 +373,7 @@ PDFViewer.prototype = { const pageDownHandler = () => { // Go to the next page if we are fit-to-page or fit-to-height. if (this.viewport_.isPagedMode()) { - this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1); + this.viewport_.goToNextPage(); // Since we do the movement of the page. e.preventDefault(); } else if (fromScriptingAPI) { @@ -410,7 +411,7 @@ PDFViewer.prototype = { // no form field is focused. if (!(this.viewport_.documentHasScrollbars().horizontal || this.isFormFieldFocused_)) { - this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1); + this.viewport_.goToPreviousPage(); // Since we do the movement of the page. e.preventDefault(); } else if (fromScriptingAPI) { @@ -431,7 +432,7 @@ PDFViewer.prototype = { // form field is focused. if (!(this.viewport_.documentHasScrollbars().horizontal || this.isFormFieldFocused_)) { - this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1); + this.viewport_.goToNextPage(); // Since we do the movement of the page. e.preventDefault(); } else if (fromScriptingAPI) { @@ -558,7 +559,7 @@ PDFViewer.prototype = { const result = await this.inkController_.save(true); await this.pluginController_.load(result.fileName, result.dataToSave); // Ensure the plugin gets the initial viewport. - this.viewport_.setZoom(this.viewport_.zoom); + this.pluginController_.afterZoom(); } }, @@ -641,7 +642,8 @@ PDFViewer.prototype = { this.isUserInitiatedEvent_ = false; this.zoomToolbar_.forceFit(params.view); if (params.viewPosition) { - const zoomedPositionShift = params.viewPosition * this.viewport_.zoom; + const zoomedPositionShift = + params.viewPosition * this.viewport_.getZoom(); const currentViewportPosition = this.viewport_.position; if (params.view == FittingType.FIT_TO_WIDTH) { currentViewportPosition.y += zoomedPositionShift; @@ -730,7 +732,7 @@ PDFViewer.prototype = { this.viewport_.position = this.lastViewportPosition_; } this.paramsParser_.getViewportFromUrlParams( - this.originalUrl_, this.handleURLParams_.bind(this)); + this.originalUrl_, params => this.handleURLParams_(params)); this.setLoadState_(LoadState.SUCCESS); this.sendDocumentLoadedMessage_(); while (this.delayedScriptingMessages_.length > 0) { @@ -1141,7 +1143,8 @@ PDFViewer.prototype = { handleNavigate: function(url, disposition) { // If in print preview, always open a new tab. if (this.isPrintPreview_) { - this.navigator_.navigate(url, Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB); + this.navigator_.navigate( + url, PdfNavigator.WindowOpenDisposition.NEW_BACKGROUND_TAB); } else { this.navigator_.navigate(url, disposition); } @@ -1306,7 +1309,7 @@ PDFViewer.prototype = { setAnnotationUndoState(state) { this.toolbar_.canUndoAnnotation = state.canUndo; this.toolbar_.canRedoAnnotation = state.canRedo; - } + }, }; /** @abstract */ @@ -1483,7 +1486,7 @@ class PluginController extends ContentController { if (this.viewport_.pinchPhase == Viewport.PinchPhase.PINCH_START) { const position = this.viewport_.position; - const zoom = this.viewport_.zoom; + const zoom = this.viewport_.getZoom(); const pinchPhase = this.viewport_.pinchPhase; this.postMessage({ type: 'viewport', @@ -1503,7 +1506,7 @@ class PluginController extends ContentController { */ afterZoom() { const position = this.viewport_.position; - const zoom = this.viewport_.zoom; + const zoom = this.viewport_.getZoom(); const pinchVector = this.viewport_.pinchPanVector || {x: 0, y: 0}; const pinchCenter = this.viewport_.pinchCenter || {x: 0, y: 0}; const pinchPhase = this.viewport_.pinchPhase; diff --git a/chromium/chrome/browser/resources/pdf/viewport.js b/chromium/chrome/browser/resources/pdf/viewport.js index 02664b3fe33..480664c014c 100644 --- a/chromium/chrome/browser/resources/pdf/viewport.js +++ b/chromium/chrome/browser/resources/pdf/viewport.js @@ -2,10 +2,26 @@ // 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; /** * 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. */ @@ -16,30 +32,36 @@ function clampZoom(factor) { } /** - * Returns the height of the intersection of two rectangles. - * - * @param {!ViewportRect} rect1 the first rect - * @param {!ViewportRect} rect2 the second rect - * @return {number} the height of the intersection of the rects + * @param {!ViewportRect} rect1 + * @param {!ViewportRect} rect2 + * @return {number} The area of the intersection of the rects */ -function getIntersectionHeight(rect1, rect2) { - return Math.max( - 0, - Math.min(rect1.y + rect1.height, rect2.y + rect2.height) - - Math.max(rect1.y, rect2.y)); +function getIntersectionArea(rect1, rect2) { + const left = Math.max(rect1.x, rect2.x); + const top = Math.max(rect1.y, rect2.y); + const right = Math.min(rect1.x + rect1.width, rect2.x + rect2.width); + const bottom = Math.min(rect1.y + rect1.height, rect2.y + rect2.height); + + if (left >= right || top >= bottom) { + return 0; + } + + return (right - left) * (bottom - top); } /** - * Computes vector between two points. - * - * @param {!Point} p1 The first point. - * @param {!Point} p2 The second point. - * @return {!Point} The vector. + * @param {!Point} p1 + * @param {!Point} p2 + * @return {!Point} The vector between the two points. */ function vectorDelta(p1, p2) { return {x: p2.x - p1.x, y: p2.y - p1.y}; } +/** + * @param {!Point} coordinateInFrame + * @return {!Point} Coordinate converted to plugin coordinates. + */ function frameToPluginCoordinate(coordinateInFrame) { const container = $('plugin'); return { @@ -48,83 +70,149 @@ function frameToPluginCoordinate(coordinateInFrame) { }; } -/** @implements {Viewport} */ -class ViewportImpl { +class Viewport { /** - * Create a new viewport. - * - * @param {Window} window the window - * @param {Object} sizer is the element which represents the size of the + * @param {!Window} window + * @param {!HTMLDivElement} sizer 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} 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) { + constructor(window, sizer, scrollbarWidth, defaultZoom, topToolbarHeight) { + /** @private {!Window} */ this.window_ = window; + + /** @private {!HTMLDivElement} */ this.sizer_ = sizer; - this.viewportChangedCallback_ = viewportChangedCallback; - this.beforeZoomCallback_ = beforeZoomCallback; - this.afterZoomCallback_ = afterZoomCallback; - this.setUserInitiatedCallback_ = setUserInitiatedCallback; + + /** @private {number} */ + this.scrollbarWidth_ = scrollbarWidth; + + /** @private {number} */ + this.defaultZoom_ = defaultZoom; + + /** @private {number} */ + this.topToolbarHeight_ = topToolbarHeight; + + /** @private {function():void} */ + this.viewportChangedCallback_ = function() {}; + + /** @private {function():void} */ + this.beforeZoomCallback_ = function() {}; + + /** @private {function():void} */ + this.afterZoomCallback_ = function() {}; + + /** @private {function(boolean):void} */ + this.userInitiatedCallback_ = function() {}; + + /** @private {boolean} */ this.allowedToChangeZoom_ = false; + + /** @private {number} */ this.internalZoom_ = 1; - this.zoomManager_ = new InactiveZoomManager(this, 1); + + /** @private {?ZoomManager} */ + this.zoomManager_ = null; + /** @private {?DocumentDimensions} */ this.documentDimensions_ = null; + /** @private {Array<ViewportRect>} */ this.pageDimensions_ = []; - this.scrollbarWidth_ = scrollbarWidth; + + /** @private {!FittingType} */ this.fittingType_ = FittingType.NONE; - this.defaultZoom_ = defaultZoom; - this.topToolbarHeight_ = topToolbarHeight; + + /** + * |twoUpView_| should be in sync with |two_up_view_| in PDFiumEngine. + * @private {boolean} + */ + this.twoUpView_ = false; + + /** @private {number} */ this.prevScale_ = 1; + + /** @private {!Viewport.PinchPhase} */ this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE; + + /** @private {?Point} */ this.pinchPanVector_ = null; + + /** @private {?Point} */ this.pinchCenter_ = null; + /** @private {?Point} */ this.firstPinchCenterInFrame_ = null; + + /** @private {number} */ this.rotations_ = 0; - // TODO(dstockwell): why isn't this private? - this.oldCenterInContent = null; - this.keepContentCentered_ = null; + + /** @private {?Point} */ + this.oldCenterInContent_ = null; + + /** @private {boolean} */ + this.keepContentCentered_ = false; + + /** @private {!EventTracker} */ + this.tracker_ = new EventTracker(); + + // Set to a default zoom manager - used in tests. + this.setZoomManager(new InactiveZoomManager(this.getZoom.bind(this), 1)); window.addEventListener('scroll', this.updateViewport_.bind(this)); window.addEventListener('resize', this.resizeWrapper_.bind(this)); } + /** @param {function():void} viewportChangedCallback */ + setViewportChangedCallback(viewportChangedCallback) { + this.viewportChangedCallback_ = viewportChangedCallback; + } + + /** @param {function():void} beforeZoomCallback */ + setBeforeZoomCallback(beforeZoomCallback) { + this.beforeZoomCallback_ = beforeZoomCallback; + } + + /** @param {function():void} afterZoomCallback */ + setAfterZoomCallback(afterZoomCallback) { + this.afterZoomCallback_ = afterZoomCallback; + } + + /** @param {function(boolean):void} userInitiatedCallback */ + setUserInitiatedCallback(userInitiatedCallback) { + this.userInitiatedCallback_ = userInitiatedCallback; + } + /** - * @param {number} n the number of clockwise 90-degree rotations to - * increment by. + * @param {number} n The number of clockwise 90-degree rotations to increment + * by. */ rotateClockwise(n) { this.rotations_ = (this.rotations_ + n) % 4; } /** - * @return {number} the number of clockwise 90-degree rotations that have been + * @return {number} The number of clockwise 90-degree rotations that have been * applied. */ getClockwiseRotations() { return this.rotations_; } + /** @param {boolean} twoUpView The new two up view state to set. */ + setTwoUpView(twoUpView) { + this.twoUpView_ = twoUpView; + } + /** * Converts a page position (e.g. the location of a bookmark) to a screen * position. - * * @param {number} page - * @param {Point} point The position on `page`. - * @return The screen position. + * @param {!Point} point The position on `page`. + * @return {!Point} The screen position. */ convertPageToScreen(page, point) { const dimensions = this.getPageInsetDimensions(page); @@ -168,9 +256,8 @@ class ViewportImpl { * Rounding is necessary when interacting with the renderer which tends to * operate in integral values (for example for determining if scrollbars * should be shown). - * * @param {number} zoom The zoom to use to compute the scaled dimensions. - * @return {Object} A dictionary with scaled 'width'/'height' of the document. + * @return {?Size} Scaled 'width' and 'height' of the document. * @private */ getZoomedDocumentDimensions_(zoom) { @@ -183,7 +270,7 @@ class ViewportImpl { }; } - /** @override */ + /** @return {!Size} A dictionary with the 'width'/'height' of the document. */ getDocumentDimensions() { return { width: this.documentDimensions_.width, @@ -192,8 +279,22 @@ class ViewportImpl { } /** - * @param {number} zoom compute whether scrollbars are needed at this zoom - * @return {{horizontal: boolean, vertical: boolean}} whether horizontal or + * @return {!ViewportRect} ViewportRect for the viewport given current zoom. + * @private + */ + getViewportRect_() { + const zoom = this.getZoom(); + return { + x: this.position.x / zoom, + y: this.position.y / zoom, + width: this.size.width / zoom, + height: this.size.height / zoom + }; + } + + /** + * @param {number} zoom Zoom to compute scrollbars for + * @return {{horizontal: boolean, vertical: boolean}} Whether horizontal or * vertical scrollbars are needed. * @private */ @@ -219,23 +320,20 @@ class ViewportImpl { } /** - * 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. + * @return {!{horizontal: boolean, vertical: boolean}} Whether horizontal and + * vertical scrollbars are needed. */ documentHasScrollbars() { - return this.documentNeedsScrollbars_(this.zoom); + return this.documentNeedsScrollbars_(this.getZoom()); } /** - * Helper function called when the zoomed document size changes. - * + * Helper function called when the zoomed document size changes. Updates the + * sizer's width and height. * @private */ contentSizeChanged_() { - const zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom); + const zoomedDimensions = this.getZoomedDocumentDimensions_(this.getZoom()); if (zoomedDimensions) { this.sizer_.style.width = zoomedDimensions.width + 'px'; this.sizer_.style.height = @@ -245,7 +343,6 @@ class ViewportImpl { /** * Called when the viewport should be updated. - * * @private */ updateViewport_() { @@ -254,18 +351,16 @@ class ViewportImpl { /** * Called when the browser window size changes. - * * @private */ resizeWrapper_() { - this.setUserInitiatedCallback_(false); + this.userInitiatedCallback_(false); this.resize_(); - this.setUserInitiatedCallback_(true); + this.userInitiatedCallback_(true); } /** * Called when the viewport size changes. - * * @private */ resize_() { @@ -282,7 +377,7 @@ class ViewportImpl { } } - /** @override */ + /** @return {!Point} The scroll position of the viewport. */ get position() { return { x: this.window_.pageXOffset, @@ -292,16 +387,15 @@ class ViewportImpl { /** * Scroll the viewport to the specified position. - * - * @param {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_); } - /** @override */ + /** @return {!Size} the size of the viewport excluding scrollbars. */ get size() { - const needsScrollbars = this.documentNeedsScrollbars_(this.zoom); + const needsScrollbars = this.documentNeedsScrollbars_(this.getZoom()); const scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0; const scrollbarHeight = needsScrollbars.horizontal ? this.scrollbarWidth_ : 0; @@ -311,22 +405,25 @@ class ViewportImpl { }; } - /** @override */ - get zoom() { + /** @return {number} The current zoom. */ + getZoom() { return this.zoomManager_.applyBrowserZoom(this.internalZoom_); } - /** - * Set the zoom manager. - * - * @type {ZoomManager} manager the zoom manager to set. - */ - set zoomManager(manager) { + /** @param {!ZoomManager} manager */ + setZoomManager(manager) { + this.resetTracker(); this.zoomManager_ = manager; + this.tracker_.add( + this.zoomManager_.getEventTarget(), 'set-zoom', + e => this.setZoom(e.detail)); + this.tracker_.add( + this.zoomManager_.getEventTarget(), 'update-zoom-from-browser', + this.updateZoomFromBrowserChange_.bind(this)); } /** - * @return {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() { @@ -334,7 +431,7 @@ class ViewportImpl { } /** - * @return {Object} The panning caused by the current pinch gesture (as + * @return {?Point} The panning caused by the current pinch gesture (as * the deltas of the x and y coordinates). */ get pinchPanVector() { @@ -342,7 +439,7 @@ class ViewportImpl { } /** - * @return {Object} The coordinates of the center of the current pinch + * @return {?Point} The coordinates of the center of the current pinch * gesture. */ get pinchCenter() { @@ -354,8 +451,7 @@ class ViewportImpl { * required so that we can notify the plugin that zooming is in progress * so that while zooming is taking place it can stop reacting to scroll events * from the viewport. This is to avoid flickering. - * - * @param {Function} f Function to wrap + * @param {function():void} f Function to wrap * @private */ mightZoom_(f) { @@ -364,12 +460,11 @@ class ViewportImpl { f(); this.allowedToChangeZoom_ = false; this.afterZoomCallback_(); + this.zoomManager_.onPdfZoomChange(); } /** - * Sets the zoom of the viewport. - * - * @param {number} newZoom the zoom level to zoom to. + * @param {number} newZoom The zoom level to set. * @private */ setZoomInternal_(newZoom) { @@ -378,26 +473,27 @@ class ViewportImpl { 'Called Viewport.setZoomInternal_ without calling ' + 'Viewport.mightZoom_.'); // Record the scroll position (relative to the top-left of the window). + let zoom = this.getZoom(); const currentScrollPos = { - x: this.position.x / this.zoom, - y: this.position.y / this.zoom + x: this.position.x / zoom, + y: this.position.y / zoom }; this.internalZoom_ = newZoom; this.contentSizeChanged_(); // Scroll to the scaled scroll position. + zoom = this.getZoom(); this.position = { - x: currentScrollPos.x * this.zoom, - y: currentScrollPos.y * this.zoom + x: currentScrollPos.x * zoom, + y: currentScrollPos.y * zoom }; } /** * Sets the zoom of the viewport. * Same as setZoomInternal_ but for pinch zoom we have some more operations. - * * @param {number} scaleDelta The zoom delta. - * @param {!Object} center The pinch center in content coordinates. + * @param {!Point} center The pinch center in content coordinates. * @private */ setPinchZoomInternal_(scaleDelta, center) { @@ -407,16 +503,17 @@ class ViewportImpl { 'Viewport.mightZoom_.'); this.internalZoom_ = clampZoom(this.internalZoom_ * scaleDelta); - const newCenterInContent = this.frameToContent(center); + const newCenterInContent = this.frameToContent_(center); const delta = { - x: (newCenterInContent.x - this.oldCenterInContent.x), - y: (newCenterInContent.y - this.oldCenterInContent.y) + x: (newCenterInContent.x - this.oldCenterInContent_.x), + y: (newCenterInContent.y - this.oldCenterInContent_.y) }; // Record the scroll position (relative to the pinch center). + const zoom = this.getZoom(); const currentScrollPos = { - x: this.position.x - delta.x * this.zoom, - y: this.position.y - delta.y * this.zoom + x: this.position.x - delta.x * zoom, + y: this.position.y - delta.y * zoom }; this.contentSizeChanged_(); @@ -426,24 +523,22 @@ class ViewportImpl { /** * Converts a point from frame to content coordinates. - * - * @param {!Object} framePoint The frame coordinates. - * @return {!Object} The content coordinates. + * @param {!Point} framePoint The frame coordinates. + * @return {!Point} The content coordinates. * @private */ - frameToContent(framePoint) { + frameToContent_(framePoint) { // TODO(mcnee) Add a helper Point class to avoid duplicating operations // on plain {x,y} objects. + const zoom = this.getZoom(); return { - x: (framePoint.x + this.position.x) / this.zoom, - y: (framePoint.y + this.position.y) / this.zoom + x: (framePoint.x + this.position.x) / zoom, + y: (framePoint.y + this.position.y) / zoom }; } /** - * Sets the zoom to the given zoom level. - * - * @param {number} newZoom the zoom level to zoom to. + * @param {number} newZoom The zoom level to zoom to. */ setZoom(newZoom) { this.fittingType_ = FittingType.NONE; @@ -453,8 +548,12 @@ class ViewportImpl { }); } - /** @override */ - updateZoomFromBrowserChange(oldBrowserZoom) { + /** + * @param {!CustomEvent<number>} e Event containing the old browser zoom. + * @private + */ + updateZoomFromBrowserChange_(e) { + const oldBrowserZoom = e.detail; this.mightZoom_(() => { // Record the scroll position (relative to the top-left of the window). const oldZoom = oldBrowserZoom * this.internalZoom_; @@ -463,34 +562,32 @@ class ViewportImpl { y: this.position.y / oldZoom }; this.contentSizeChanged_(); + const newZoom = this.getZoom(); // Scroll to the scaled scroll position. this.position = { - x: currentScrollPos.x * this.zoom, - y: currentScrollPos.y * this.zoom + x: currentScrollPos.x * newZoom, + y: currentScrollPos.y * newZoom }; this.updateViewport_(); }); } - /** - * @return {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_; } - /** - * @return {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. - * - * @param {number} y the y-coordinate to get the page at. - * @return {number} the index of a page overlapping the given y-coordinate. + * Get the page at a given y position. If there are multiple pages + * overlapping the given y-coordinate, return the page with the smallest + * index. + * @param {number} y The y-coordinate to get the page at. + * @return {number} The index of a page overlapping the given y-coordinate. * @private */ getPageAtY_(y) { @@ -521,9 +618,32 @@ class ViewportImpl { return 0; } - /** @override */ + /** + * Return the last page visible in the viewport. Returns the last index of the + * document if the viewport is below the document. + * @param {!ViewportRect} viewportRect + * @return {number} The highest index of the pages visible in the viewport. + * @private + */ + getLastPageInViewport_(viewportRect) { + const pageAtY = this.getPageAtY_(viewportRect.y + viewportRect.height); + + if (!this.twoUpView_ || pageAtY % 2 == 1 || + pageAtY + 1 >= this.pageDimensions_.length) { + return pageAtY; + } + + const nextPage = this.pageDimensions_[pageAtY + 1]; + return getIntersectionArea(viewportRect, nextPage) > 0 ? pageAtY + 1 : + pageAtY; + } + + /** + * @param {!Point} point + * @return {boolean} Whether |point| (in screen coordinates) is inside a page + */ isPointInsidePage(point) { - const zoom = this.zoom; + const zoom = this.getZoom(); const size = this.size; const position = this.position; const page = this.getPageAtY_((position.y + point.y) / zoom); @@ -544,48 +664,51 @@ class ViewportImpl { } /** - * Returns the page with the greatest proportion of its height in the current - * viewport. - * - * @return {number} the index of the most visible page. + * @return {number} The index of the page with the greatest proportion of its + * area in the current viewport. */ getMostVisiblePage() { - const firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom); - if (firstVisiblePage == this.pageDimensions_.length - 1) { + const viewportRect = this.getViewportRect_(); + + const firstVisiblePage = this.getPageAtY_(viewportRect.y); + const lastPossibleVisiblePage = this.getLastPageInViewport_(viewportRect); + if (firstVisiblePage === lastPossibleVisiblePage) { return firstVisiblePage; } - const 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 - }; - const firstVisiblePageVisibility = - getIntersectionHeight( - this.pageDimensions_[firstVisiblePage], viewportRect) / - this.pageDimensions_[firstVisiblePage].height; - const nextPageVisibility = - getIntersectionHeight( - this.pageDimensions_[firstVisiblePage + 1], viewportRect) / - this.pageDimensions_[firstVisiblePage + 1].height; - if (nextPageVisibility > firstVisiblePageVisibility) { - return firstVisiblePage + 1; + let mostVisiblePage = firstVisiblePage; + let largestIntersection = 0; + + for (let i = firstVisiblePage; i < lastPossibleVisiblePage + 1; i++) { + const pageArea = + this.pageDimensions_[i].width * this.pageDimensions_[i].height; + + // TODO(thestig): check whether we can remove this check. + if (pageArea <= 0) { + continue; + } + + const pageIntersectionArea = + getIntersectionArea(this.pageDimensions_[i], viewportRect) / pageArea; + + if (pageIntersectionArea > largestIntersection) { + mostVisiblePage = i; + largestIntersection = pageIntersectionArea; + } } - return firstVisiblePage; + + return mostVisiblePage; } /** * Compute the zoom level for fit-to-page, fit-to-width or fit-to-height. - * * At least one of {fitWidth, fitHeight} must be true. - * - * @param {Object} pageDimensions the dimensions of a given page in px. - * @param {boolean} fitWidth a bool indicating whether the whole width of the - * page needs to be in the viewport. - * @param {boolean} fitHeight a bool indicating whether the whole height of - * the page needs to be in the viewport. - * @return {number} the internal zoom to set + * @param {!Size} pageDimensions The dimensions of a given page in px. + * @param {boolean} fitWidth Whether the whole width of the page needs to be + * in the viewport. + * @param {boolean} fitHeight Whether the whole height of the page needs to be + * in the viewport. + * @return {number} The internal zoom to set * @private */ computeFittingZoom_(pageDimensions, fitWidth, fitHeight) { @@ -644,16 +767,15 @@ class ViewportImpl { /** * Compute a zoom level given the dimensions to fit and the actual numbers * in those dimensions. - * - * @param {boolean} fitWidth make sure the page width is totally contained in - * the window. - * @param {boolean} fitHeight make sure the page height is totally contained - * in the window. - * @param {number} windowWidth the width of the window in px. - * @param {number} windowHeight the height of the window in px. - * @param {number} pageWidth the width of the page in px. - * @param {number} pageHeight the height of the page in px. - * @return {number} the internal zoom to set + * @param {boolean} fitWidth Whether to constrain the page width to the + * window. + * @param {boolean} fitHeight Whether to constrain the page height to the + * window. + * @param {number} windowWidth Width of the window in px. + * @param {number} windowHeight Height of the window in px. + * @param {number} pageWidth Width of the page in px. + * @param {number} pageHeight Height of the page in px. + * @return {number} The internal zoom to set * @private */ computeFittingZoomGivenDimensions_( @@ -683,9 +805,7 @@ class ViewportImpl { return Math.max(zoom, 0); } - /** - * Zoom the viewport so that the page width consumes the entire viewport. - */ + /** Zoom the viewport so that the page width consumes the entire viewport. */ fitToWidth() { this.mightZoom_(() => { this.fittingType_ = FittingType.FIT_TO_WIDTH; @@ -702,7 +822,6 @@ class ViewportImpl { /** * Zoom the viewport so that the page height consumes the entire viewport. - * * @param {boolean} scrollToTopOfPage Set to true if the viewport should be * scrolled to the top of the current page. Set to false if the viewport * should remain at the current scroll position. @@ -723,25 +842,25 @@ class ViewportImpl { }; this.setZoomInternal_(this.computeFittingZoom_(dimensions, false, true)); if (scrollToTopOfPage) { - this.position = {x: 0, y: this.pageDimensions_[page].y * this.zoom}; + this.position = { + x: 0, + y: this.pageDimensions_[page].y * this.getZoom() + }; } this.updateViewport_(); }); } - /** - * Zoom the viewport so that the page height consumes the entire viewport. - */ + /** Zoom the viewport so that the page height consumes the entire viewport. */ fitToHeight() { this.fitToHeightInternal_(true); } /** * Zoom the viewport so that a page consumes as much as possible of the it. - * - * @param {boolean} scrollToTopOfPage Set to true if the viewport should be - * scrolled to the top of the current page. Set to false if the viewport - * should remain at the current scroll position. + * @param {boolean} scrollToTopOfPage Whether the viewport should be scrolled + * to the top of the current page. If false, the viewport will remain at + * the current scroll position. * @private */ fitToPageInternal_(scrollToTopOfPage) { @@ -758,7 +877,10 @@ class ViewportImpl { }; this.setZoomInternal_(this.computeFittingZoom_(dimensions, true, true)); if (scrollToTopOfPage) { - this.position = {x: 0, y: this.pageDimensions_[page].y * this.zoom}; + this.position = { + x: 0, + y: this.pageDimensions_[page].y * this.getZoom() + }; } this.updateViewport_(); }); @@ -772,9 +894,7 @@ class ViewportImpl { this.fitToPageInternal_(true); } - /** - * Zoom the viewport to the default zoom policy. - */ + /** Zoom the viewport to the default zoom. */ fitToNone() { this.mightZoom_(() => { this.fittingType_ = FittingType.NONE; @@ -788,9 +908,7 @@ class ViewportImpl { }); } - /** - * Zoom out to the next predefined zoom level. - */ + /** Zoom out to the next predefined zoom level. */ zoomOut() { this.mightZoom_(() => { this.fittingType_ = FittingType.NONE; @@ -805,9 +923,7 @@ class ViewportImpl { }); } - /** - * Zoom in to the next predefined zoom level. - */ + /** Zoom in to the next predefined zoom level. */ zoomIn() { this.mightZoom_(() => { this.fittingType_ = FittingType.NONE; @@ -824,7 +940,6 @@ class ViewportImpl { /** * Pinch zoom event handler. - * * @param {!Object} e The pinch event. */ pinchZoom(e) { @@ -857,8 +972,8 @@ class ViewportImpl { y: this.window_.innerHeight / 2 }; } else if (this.keepContentCentered_) { - this.oldCenterInContent = - this.frameToContent(frameToPluginCoordinate(e.center)); + this.oldCenterInContent_ = + this.frameToContent_(frameToPluginCoordinate(e.center)); this.keepContentCentered_ = false; } @@ -872,10 +987,10 @@ class ViewportImpl { pinchZoomStart(e) { this.pinchPhase_ = Viewport.PinchPhase.PINCH_START; this.prevScale_ = 1; - this.oldCenterInContent = - this.frameToContent(frameToPluginCoordinate(e.center)); + this.oldCenterInContent_ = + this.frameToContent_(frameToPluginCoordinate(e.center)); - const needsScrollbars = this.documentNeedsScrollbars_(this.zoom); + const needsScrollbars = this.documentNeedsScrollbars_(this.getZoom()); this.keepContentCentered_ = !needsScrollbars.horizontal; // We keep track of begining of the pinch. // By doing so we will be able to compute the pan distance. @@ -887,7 +1002,7 @@ class ViewportImpl { this.mightZoom_(() => { this.pinchPhase_ = Viewport.PinchPhase.PINCH_END; const scaleDelta = e.startScaleRatio / this.prevScale_; - this.pinchCenter_ = e.center; + this.pinchCenter_ = /** @type {!Point} */ (e.center); this.setPinchZoomInternal_(scaleDelta, frameToPluginCoordinate(e.center)); this.updateViewport_(); @@ -900,8 +1015,32 @@ class ViewportImpl { } /** + * Go to the next page. If the document is in two-up view, go to the left page + * of the next row. + */ + goToNextPage() { + const currentPage = this.getMostVisiblePage(); + const nextPageOffset = (this.twoUpView_ && currentPage % 2 == 0) ? 2 : 1; + this.goToPage(currentPage + nextPageOffset); + } + + /** + * Go to the previous page. If the document is in two-up view, go to the left + * page of the previous row. + */ + goToPreviousPage() { + const currentPage = this.getMostVisiblePage(); + let previousPageOffset = -1; + + if (this.twoUpView_) { + previousPageOffset = (currentPage % 2 == 0) ? -2 : -3; + } + + this.goToPage(currentPage + previousPageOffset); + } + + /** * Go to the given page index. - * * @param {number} page the index of the page to go to. zero-based. */ goToPage(page) { @@ -910,7 +1049,6 @@ class ViewportImpl { /** * Go to the given y position in the given page index. - * * @param {number} page the index of the page to go to. zero-based. * @param {number} x the x position in the page to go to. * @param {number} y the y position in the page to go to. @@ -935,17 +1073,15 @@ class ViewportImpl { toolbarOffset = this.topToolbarHeight_; } this.position = { - x: (dimensions.x + x) * this.zoom, - y: (dimensions.y + y) * this.zoom - toolbarOffset + x: (dimensions.x + x) * this.getZoom(), + y: (dimensions.y + y) * this.getZoom() - toolbarOffset }; this.updateViewport_(); }); } /** - * Set the dimensions of the document. - * - * @param {DocumentDimensions} documentDimensions the dimensions of the + * @param {DocumentDimensions} documentDimensions The dimensions of the * document */ setDocumentDimensions(documentDimensions) { @@ -982,9 +1118,8 @@ class ViewportImpl { /** * 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. + * @param {number} page The index of the page to get the rect for. + * @return {!ViewportRect} A rect representing the page in screen coordinates. */ getPageScreenRect(page) { if (!this.documentDimensions_) { @@ -1006,24 +1141,23 @@ class ViewportImpl { Viewport.PAGE_SHADOW.left; // Compute the space on the left of the document if the document fits // completely in the screen. + const zoom = this.getZoom(); let spaceOnLeft = - (this.size.width - this.documentDimensions_.width * this.zoom) / 2; + (this.size.width - this.documentDimensions_.width * 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 + x: x * zoom + spaceOnLeft - this.window_.pageXOffset, + y: insetDimensions.y * zoom - this.window_.pageYOffset, + width: insetDimensions.width * zoom, + height: insetDimensions.height * zoom }; } /** * Check if the current fitting type is a paged mode. - * * In a paged mode, page up and page down scroll to the top of the * previous/next page and part of the page is under the toolbar. - * * @return {boolean} Whether the current fitting type is a paged mode. */ isPagedMode() { @@ -1032,11 +1166,7 @@ class ViewportImpl { this.fittingType_ == FittingType.FIT_TO_HEIGHT); } - /** - * Scroll the viewport to the specified position. - * - * @param {!Point} point The position to which to move the viewport. - */ + /** @param {!Point} point The position to which to scroll the viewport. */ scrollTo(point) { let changed = false; const newPosition = this.position; @@ -1054,15 +1184,64 @@ class ViewportImpl { } } - /** - * Scroll the viewport by the specified delta. - * - * @param {!Point} delta The delta by which to move the viewport. - */ + /** @param {!Point} delta The delta by which to scroll the viewport. */ scrollBy(delta) { const newPosition = this.position; newPosition.x += delta.x; newPosition.y += delta.y; this.scrollTo(newPosition); } + + /** Removes all events being tracked from the tracker. */ + resetTracker() { + if (this.tracker_) { + this.tracker_.removeAll(); + } + } } + +/** + * 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/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 +}; diff --git a/chromium/chrome/browser/resources/pdf/viewport_interface.js b/chromium/chrome/browser/resources/pdf/viewport_interface.js deleted file mode 100644 index c9f99aceb2f..00000000000 --- a/chromium/chrome/browser/resources/pdf/viewport_interface.js +++ /dev/null @@ -1,136 +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 {{ - * 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 -}; diff --git a/chromium/chrome/browser/resources/pdf/zoom_manager.js b/chromium/chrome/browser/resources/pdf/zoom_manager.js index 6c3182d3fd1..db2878dd3d9 100644 --- a/chromium/chrome/browser/resources/pdf/zoom_manager.js +++ b/chromium/chrome/browser/resources/pdf/zoom_manager.js @@ -11,35 +11,50 @@ */ class ZoomManager { /** - * @param {!Viewport} viewport A Viewport for which to manage zoom. + * @param {function():number} getViewportZoom Callback to get the viewport's + * current zoom level. * @param {number} initialZoom The initial browser zoom level. */ - constructor(viewport, initialZoom) { + constructor(getViewportZoom, initialZoom) { if (this.constructor === ZoomManager) { throw new TypeError('Instantiated abstract class: ZoomManager'); } - this.viewport_ = viewport; + + /** @private {number} */ this.browserZoom_ = initialZoom; + + /** @private {function():number} */ + this.getViewportZoom_ = getViewportZoom; + + /** @private {!EventTarget} */ + this.eventTarget_ = new cr.EventTarget(); + } + + /** @return {!EventTarget} */ + getEventTarget() { + return this.eventTarget_; } /** * Creates the appropriate kind of zoom manager given the zoom behavior. * * @param {BrowserApi.ZoomBehavior} zoomBehavior How to manage zoom. - * @param {!Viewport} viewport A Viewport for which to manage zoom. - * @param {Function} setBrowserZoomFunction A function that sets the browser - * zoom to the provided value. + * @param {function():number} getViewportZoom A function that gets the current + * viewport zoom. + * @param {function(number):Promise} setBrowserZoomFunction A function that + * sets the browser zoom to the provided value. * @param {number} initialZoom The initial browser zoom level. */ - static create(zoomBehavior, viewport, setBrowserZoomFunction, initialZoom) { + static create( + zoomBehavior, getViewportZoom, setBrowserZoomFunction, initialZoom) { switch (zoomBehavior) { case BrowserApi.ZoomBehavior.MANAGE: return new ActiveZoomManager( - viewport, setBrowserZoomFunction, initialZoom); + getViewportZoom, setBrowserZoomFunction, initialZoom); case BrowserApi.ZoomBehavior.PROPAGATE_PARENT: - return new EmbeddedZoomManager(viewport, initialZoom); + return new EmbeddedZoomManager(getViewportZoom, initialZoom); default: - return new InactiveZoomManager(viewport, initialZoom); + return new InactiveZoomManager(getViewportZoom, initialZoom); } } @@ -104,15 +119,19 @@ class InactiveZoomManager extends ZoomManager {} class ActiveZoomManager extends ZoomManager { /** * Constructs a ActiveZoomManager. - * - * @param {!Viewport} viewport A Viewport for which to manage zoom. - * @param {Function} setBrowserZoomFunction A function that sets the browser - * zoom to the provided value. + * @param {function():number} getViewportZoom A function that gets the current + * viewport zoom level + * @param {function(number):Promise} setBrowserZoomFunction A function that + * sets the browser zoom to the provided value. * @param {number} initialZoom The initial browser zoom level. */ - constructor(viewport, setBrowserZoomFunction, initialZoom) { - super(viewport, initialZoom); + constructor(getViewportZoom, setBrowserZoomFunction, initialZoom) { + super(getViewportZoom, initialZoom); + + /** @private {function(number):Promise} */ this.setBrowserZoomFunction_ = setBrowserZoomFunction; + + /** @private {?Promise} */ this.changingBrowserZoom_ = null; } @@ -135,11 +154,13 @@ class ActiveZoomManager extends ZoomManager { } this.browserZoom_ = newZoom; - this.viewport_.setZoom(newZoom); + this.eventTarget_.dispatchEvent( + new CustomEvent('set-zoom', {detail: newZoom})); } /** * Invoked when an extension-initiated zoom-level change occurs. + * @override */ onPdfZoomChange() { // If we are already changing the browser zoom level in response to a @@ -150,20 +171,21 @@ class ActiveZoomManager extends ZoomManager { return; } - const zoom = this.viewport_.zoom; - if (this.floatingPointEquals(this.browserZoom_, zoom)) { + const viewportZoom = this.getViewportZoom_(); + if (this.floatingPointEquals(this.browserZoom_, viewportZoom)) { return; } - this.changingBrowserZoom_ = this.setBrowserZoomFunction_(zoom).then(() => { - this.browserZoom_ = zoom; - this.changingBrowserZoom_ = null; + this.changingBrowserZoom_ = + this.setBrowserZoomFunction_(viewportZoom).then(() => { + this.browserZoom_ = viewportZoom; + this.changingBrowserZoom_ = null; - // The extension's zoom level may have changed while the browser zoom - // change was in progress. We call back into onPdfZoomChange to ensure - // the browser zoom is up to date. - this.onPdfZoomChange(); - }); + // The extension's zoom level may have changed while the browser zoom + // change was in progress. We call back into onPdfZoomChange to ensure + // the browser zoom is up to date. + this.onPdfZoomChange(); + }); } /** @@ -206,6 +228,7 @@ class EmbeddedZoomManager extends ZoomManager { onBrowserZoomChange(newZoom) { const oldZoom = this.browserZoom_; this.browserZoom_ = newZoom; - this.viewport_.updateZoomFromBrowserChange(oldZoom); + this.eventTarget_.dispatchEvent( + new CustomEvent('update-zoom-from-browser', {detail: oldZoom})); } } |