diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 17:21:03 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 16:25:15 +0000 |
commit | c551f43206405019121bd2b2c93714319a0a3300 (patch) | |
tree | 1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/chrome/browser/resources/pdf | |
parent | 7961cea6d1041e3e454dae6a1da660b453efd238 (diff) |
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/chrome/browser/resources/pdf')
48 files changed, 1614 insertions, 1278 deletions
diff --git a/chromium/chrome/browser/resources/pdf/BUILD.gn b/chromium/chrome/browser/resources/pdf/BUILD.gn index 89a0223ca86..079381842d8 100644 --- a/chromium/chrome/browser/resources/pdf/BUILD.gn +++ b/chromium/chrome/browser/resources/pdf/BUILD.gn @@ -4,28 +4,19 @@ import("//third_party/closure_compiler/compile_js.gni") -# TODO(dpapad): Add compile targets for all files, crbug.com/721073. group("closure_compile") { deps = [ ":pdf_resources", - "elements/viewer-bookmark:closure_compile", - "elements/viewer-error-screen:closure_compile", - "elements/viewer-form-warning:closure_compile", - "elements/viewer-page-indicator:closure_compile", - "elements/viewer-page-selector:closure_compile", - "elements/viewer-password-screen:closure_compile", - "elements/viewer-pdf-toolbar:closure_compile", - "elements/viewer-toolbar-dropdown:closure_compile", - "elements/viewer-zoom-toolbar:closure_compile", + "elements:closure_compile", ] if (is_chromeos) { - deps += [ - "elements/viewer-ink-host:closure_compile", - "ink:closure_compile", - ] + deps += [ "ink:closure_compile" ] } } +js_library("annotation_tool") { +} + js_library("browser_api") { deps = [ "//ui/webui/resources/js:assert", @@ -86,15 +77,53 @@ js_library("navigator") { ] } +js_library("toolbar_manager") { + deps = [ + "elements:viewer-pdf-toolbar", + "elements:viewer-zoom-toolbar", + ] +} + +js_library("controller") { + deps = [ + ":annotation_tool", + ":viewport", + "elements:viewer-pdf-toolbar", + "//ui/webui/resources/js:load_time_data", + "//ui/webui/resources/js/cr:event_target", + ] +} + +js_library("pdf_viewer") { + deps = [ + ":controller", + ":navigator", + ":toolbar_manager", + ":viewport", + "elements:viewer-bookmark", + "elements:viewer-error-screen", + "elements:viewer-page-indicator", + "elements:viewer-password-screen", + "elements:viewer-pdf-toolbar", + "elements:viewer-zoom-toolbar", + "//ui/webui/resources/js:event_tracker", + "//ui/webui/resources/js:load_time_data", + ] + externs_list = [ "$externs_path/resources_private.js" ] +} + js_type_check("pdf_resources") { deps = [ ":browser_api", + ":controller", ":gesture_detector", ":metrics", ":navigator", ":open_pdf_params_parser", ":pdf_fitting_type", ":pdf_scripting_api", + ":pdf_viewer", + ":toolbar_manager", ":viewport", ":viewport_scroller", ":zoom_manager", diff --git a/chromium/chrome/browser/resources/pdf/annotation_tool.js b/chromium/chrome/browser/resources/pdf/annotation_tool.js new file mode 100644 index 00000000000..5e887b048b7 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/annotation_tool.js @@ -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. + +// @fileoverview This file is not included in the build. Only used for type +// checking purposes. + +/** + * @typedef {{ + * tool: string, + * size: number, + * color: (string|null), + * }} + */ +let AnnotationTool; diff --git a/chromium/chrome/browser/resources/pdf/controller.js b/chromium/chrome/browser/resources/pdf/controller.js new file mode 100644 index 00000000000..ea790f32d0f --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/controller.js @@ -0,0 +1,494 @@ +// 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. + +'use strict'; + +/** @typedef {{ type: string }} */ +let MessageData; + +/** + * @typedef {{ + * dataToSave: Array, + * token: string, + * fileName: string + * }} + */ +let SaveDataMessageData; + +/** + * @typedef {{ + * type: string, + * to: string, + * cc: string, + * bcc: string, + * subject: string, + * body: string, + * }} + */ +let EmailMessageData; + +/** + * @typedef {{ + * type: string, + * url: string, + * grayscale: boolean, + * modifiable: boolean, + * pageNumbers: !Array<number> + * }} + */ +let PrintPreviewParams; + +// Note: Redefining this type here, to work around the fact that ink externs +// are only available on Chrome OS, so the targets that contain them cannot be +// built on other platforms. +// TODO (rbpotter): Break InkController into its own file that is only included +// on Chrome OS. + +/** + * @typedef {{ + * setAnnotationTool: function(AnnotationTool):void, + * viewportChanged: function():void, + * saveDocument: function():!Promise, + * undo: function():void, + * redo: function():void, + * load: function(string, !ArrayBuffer):!Promise, + * viewport: !Viewport, + * }} + */ +let ViewerInkHostElement; + +/** + * Creates a cryptographically secure pseudorandom 128-bit token. + * @return {string} The generated token as a hex string. + */ +function createToken() { + const randomBytes = new Uint8Array(16); + return window.crypto.getRandomValues(randomBytes) + .map(b => b.toString(16).padStart(2, '0')) + .join(''); +} + +/** @abstract */ +class ContentController { + constructor() {} + + beforeZoom() {} + + afterZoom() {} + + viewportChanged() {} + + /** @abstract */ + rotateClockwise() {} + + /** @abstract */ + rotateCounterclockwise() {} + + /** Triggers printing of the current document. */ + print() {} + + /** Undo an edit action. */ + undo() {} + + /** Redo an edit action. */ + redo() {} + + /** + * Requests that the current document be saved. + * @param {boolean} requireResult whether a response is required, otherwise + * the controller may save the document to disk internally. + * @return {Promise<{fileName: string, dataToSave: ArrayBuffer}>} + * @abstract + */ + save(requireResult) {} + + /** + * Loads PDF document from `data` activates UI. + * @param {string} fileName + * @param {!ArrayBuffer} data + * @return {Promise<void>} + * @abstract + */ + load(fileName, data) {} + + /** + * Unloads the current document and removes the UI. + * @abstract + */ + unload() {} +} + +/** + * Controller for annotation mode, on Chrome OS only. Fires the following events + * from its event target: + * has-unsaved-changes: Fired to indicate there are ink annotations that have + * not been saved. + * set-annotation-undo-state: Contains information about whether undo or redo + * options are available. + */ +class InkController extends ContentController { + /** @param {!Viewport} viewport */ + constructor(viewport) { + super(); + + /** @private {!Viewport} */ + this.viewport_ = viewport; + + /** @private {?ViewerInkHostElement} */ + this.inkHost_ = null; + + /** @private {!EventTarget} */ + this.eventTarget_ = new cr.EventTarget(); + + /** @type {?AnnotationTool} */ + this.tool_ = null; + } + + /** @return {!EventTarget} */ + getEventTarget() { + return this.eventTarget_; + } + + /** @param {AnnotationTool} tool */ + setAnnotationTool(tool) { + this.tool_ = tool; + if (this.inkHost_) { + this.inkHost_.setAnnotationTool(tool); + } + } + + /** @override */ + rotateClockwise() { + // TODO(dstockwell): implement rotation + } + + /** @override */ + rotateCounterclockwise() { + // TODO(dstockwell): implement rotation + } + + /** @override */ + viewportChanged() { + this.inkHost_.viewportChanged(); + } + + /** @override */ + save(requireResult) { + return this.inkHost_.saveDocument(); + } + + /** @override */ + undo() { + this.inkHost_.undo(); + } + + /** @override */ + redo() { + this.inkHost_.redo(); + } + + /** @override */ + load(filename, data) { + if (!this.inkHost_) { + const inkHost = document.createElement('viewer-ink-host'); + $('content').appendChild(inkHost); + this.inkHost_ = /** @type {!ViewerInkHostElement} */ (inkHost); + this.inkHost_.viewport = this.viewport_; + inkHost.addEventListener('stroke-added', e => { + this.eventTarget_.dispatchEvent(new CustomEvent('has-unsaved-changes')); + }); + inkHost.addEventListener('undo-state-changed', e => { + this.eventTarget_.dispatchEvent( + new CustomEvent('set-annotation-undo-state', {detail: e.detail})); + }); + } + return this.inkHost_.load(filename, data); + } + + /** @override */ + unload() { + this.inkHost_.remove(); + this.inkHost_ = null; + } +} + +/** + * PDF plugin controller, responsible for communicating with the embedded plugin + * element. Dispatches a 'plugin-message' event containing the message from the + * plugin, if a message type not handled by this controller is received. + */ +class PluginController extends ContentController { + /** + * @param {!HTMLEmbedElement} plugin + * @param {!Viewport} viewport + * @param {function():boolean} getIsUserInitiatedCallback + * @param {function():?Promise} getLoadedCallback + */ + constructor(plugin, viewport, getIsUserInitiatedCallback, getLoadedCallback) { + super(); + + /** @private {!HTMLEmbedElement} */ + this.plugin_ = plugin; + + /** @private {!Viewport} */ + this.viewport_ = viewport; + + /** @private {!function():boolean} */ + this.getIsUserInitiatedCallback_ = getIsUserInitiatedCallback; + + /** @private {!function():?Promise} */ + this.getLoadedCallback_ = getLoadedCallback; + + /** @private {!Map<string, PromiseResolver>} */ + this.pendingTokens_ = new Map(); + this.plugin_.addEventListener( + 'message', e => this.handlePluginMessage_(e), false); + + /** @private {!EventTarget} */ + this.eventTarget_ = new cr.EventTarget(); + } + + /** @return {!EventTarget} */ + getEventTarget() { + return this.eventTarget_; + } + + /** + * Notify the plugin to stop reacting to scroll events while zoom is taking + * place to avoid flickering. + * @override + */ + beforeZoom() { + this.postMessage_({type: 'stopScrolling'}); + + if (this.viewport_.pinchPhase == Viewport.PinchPhase.PINCH_START) { + const position = this.viewport_.position; + const zoom = this.viewport_.getZoom(); + const pinchPhase = this.viewport_.pinchPhase; + const layoutOptions = this.viewport_.getLayoutOptions(); + this.postMessage_({ + type: 'viewport', + userInitiated: true, + zoom: zoom, + layoutOptions: layoutOptions, + xOffset: position.x, + yOffset: position.y, + pinchPhase: pinchPhase + }); + } + } + + /** + * Notify the plugin of the zoom change and to continue reacting to scroll + * events. + * @override + */ + afterZoom() { + const position = this.viewport_.position; + const zoom = this.viewport_.getZoom(); + const layoutOptions = this.viewport_.getLayoutOptions(); + const pinchVector = this.viewport_.pinchPanVector || {x: 0, y: 0}; + const pinchCenter = this.viewport_.pinchCenter || {x: 0, y: 0}; + const pinchPhase = this.viewport_.pinchPhase; + + this.postMessage_({ + type: 'viewport', + userInitiated: this.getIsUserInitiatedCallback_(), + zoom: zoom, + layoutOptions: layoutOptions, + xOffset: position.x, + yOffset: position.y, + pinchPhase: pinchPhase, + pinchX: pinchCenter.x, + pinchY: pinchCenter.y, + pinchVectorX: pinchVector.x, + pinchVectorY: pinchVector.y + }); + } + + /** + * Post a message to the PPAPI plugin. Some messages will cause an async reply + * to be received through handlePluginMessage_(). + * @param {!MessageData} message Message to post. + * @private + */ + postMessage_(message) { + this.plugin_.postMessage(message); + } + + /** @override */ + rotateClockwise() { + this.postMessage_({type: 'rotateClockwise'}); + } + + /** @override */ + rotateCounterclockwise() { + this.postMessage_({type: 'rotateCounterclockwise'}); + } + + /** @override */ + print() { + this.postMessage_({type: 'print'}); + } + + selectAll() { + this.postMessage_({type: 'selectAll'}); + } + + getSelectedText() { + this.postMessage_({type: 'getSelectedText'}); + } + + /** @param {!PrintPreviewParams} printPreviewParams */ + resetPrintPreviewMode(printPreviewParams) { + this.postMessage_({ + type: 'resetPrintPreviewMode', + url: printPreviewParams.url, + grayscale: printPreviewParams.grayscale, + // If the PDF isn't modifiable we send 0 as the page count so that no + // blank placeholder pages get appended to the PDF. + pageCount: + (printPreviewParams.modifiable ? + printPreviewParams.pageNumbers.length : + 0) + }); + } + + /** @param {string} newColor New color, in hex, for the PDF plugin. */ + backgroundColorChanged(newColor) { + this.postMessage_({ + type: 'backgroundColorChanged', + backgroundColor: newColor, + }); + } + + /** + * @param {string} url + * @param {number} index + */ + loadPreviewPage(url, index) { + this.postMessage_({type: 'loadPreviewPage', url: url, index: index}); + } + + /** @param {string} password */ + getPasswordComplete(password) { + this.postMessage_({type: 'getPasswordComplete', password: password}); + } + + /** @param {string} destination */ + getNamedDestination(destination) { + this.postMessage_({ + type: 'getNamedDestination', + namedDestination: destination, + }); + } + + /** @override */ + save(requireResult) { + const resolver = new PromiseResolver(); + const newToken = createToken(); + this.pendingTokens_.set(newToken, resolver); + this.postMessage_({type: 'save', token: newToken, force: requireResult}); + return resolver.promise; + } + + /** @override */ + async load(fileName, data) { + const url = URL.createObjectURL(new Blob([data])); + this.plugin_.removeAttribute('headers'); + this.plugin_.setAttribute('stream-url', url); + this.plugin_.style.display = 'block'; + try { + await this.getLoadedCallback_(); + } finally { + URL.revokeObjectURL(url); + } + } + + /** @override */ + unload() { + this.plugin_.style.display = 'none'; + } + + /** + * An event handler for handling message events received from the plugin. + * @param {!Event} messageEvent a message event. + * @private + */ + handlePluginMessage_(messageEvent) { + const messageData = /** @type {!MessageData} */ (messageEvent.data); + switch (messageData.type) { + case 'email': + const emailData = /** @type {!EmailMessageData} */ (messageData); + const href = 'mailto:' + emailData.to + '?cc=' + emailData.cc + + '&bcc=' + emailData.bcc + '&subject=' + emailData.subject + + '&body=' + emailData.body; + window.location.href = href; + break; + case 'goToPage': + this.viewport_.goToPage( + /** @type {{type: string, page: number}} */ (messageData).page); + break; + case 'setScrollPosition': + this.viewport_.scrollTo(/** @type {!PartialPoint} */ (messageData)); + break; + case 'scrollBy': + this.viewport_.scrollBy(/** @type {!Point} */ (messageData)); + break; + case 'saveData': + this.saveData_(/** @type {!SaveDataMessageData} */ (messageData)); + break; + case 'consumeSaveToken': + const saveTokenData = + /** @type {{ type: string, token: string }} */ (messageData); + const resolver = this.pendingTokens_.get(saveTokenData.token); + assert(this.pendingTokens_.delete(saveTokenData.token)); + resolver.resolve(null); + break; + default: + this.eventTarget_.dispatchEvent( + new CustomEvent('plugin-message', {detail: messageData})); + } + } + + /** + * Handles the pdf file buffer received from the plugin. + * + * @param {!SaveDataMessageData} messageData data of the message event. + * @private + */ + saveData_(messageData) { + assert( + loadTimeData.getBoolean('pdfFormSaveEnabled') || + loadTimeData.getBoolean('pdfAnnotationsEnabled')); + + // Verify a token that was created by this instance is included to avoid + // being spammed. + const resolver = this.pendingTokens_.get(messageData.token); + assert(this.pendingTokens_.delete(messageData.token)); + + if (!messageData.dataToSave) { + resolver.reject(); + return; + } + + // Verify the file size and the first bytes to make sure it's a PDF. Cap at + // 100 MB. This cap should be kept in sync with and is also enforced in + // pdf/out_of_process_instance.cc. + const MIN_FILE_SIZE = '%PDF1.0'.length; + const MAX_FILE_SIZE = 100 * 1000 * 1000; + + const buffer = messageData.dataToSave; + const bufView = new Uint8Array(buffer); + assert( + bufView.length <= MAX_FILE_SIZE, + `File too large to be saved: ${bufView.length} bytes.`); + assert(bufView.length >= MIN_FILE_SIZE); + assert( + String.fromCharCode(bufView[0], bufView[1], bufView[2], bufView[3]) == + '%PDF'); + + resolver.resolve(messageData); + } +} diff --git a/chromium/chrome/browser/resources/pdf/elements/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/BUILD.gn new file mode 100644 index 00000000000..248059ae74f --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/BUILD.gn @@ -0,0 +1,118 @@ +# 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") +import("//tools/polymer/polymer.gni") + +js_type_check("closure_compile") { + deps = [ + ":viewer-bookmark", + ":viewer-error-screen", + ":viewer-page-indicator", + ":viewer-page-selector", + ":viewer-password-screen", + ":viewer-pdf-toolbar", + ":viewer-toolbar-dropdown", + ":viewer-zoom-button", + ":viewer-zoom-toolbar", + ] + if (is_chromeos) { + deps += [ + ":viewer-form-warning", + ":viewer-ink-host", + ":viewer-pen-options", + ] + } +} + +js_library("viewer-bookmark") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior:iron-a11y-keys-behavior-extracted", + ] +} + +js_library("viewer-error-screen") { + deps = [ + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + ] +} + +if (is_chromeos) { + js_library("viewer-form-warning") { + deps = [ + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:promise_resolver", + ] + } + + js_library("viewer-ink-host") { + deps = [ + "//chrome/browser/resources/pdf:metrics", + "//chrome/browser/resources/pdf:viewport", + "//chrome/browser/resources/pdf/ink:ink_api", + ] + } +} + +js_library("viewer-page-indicator") { + deps = [ + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:util", + ] +} + +js_library("viewer-page-selector") { + deps = [ + "//ui/webui/resources/cr_elements/cr_input:cr_input", + ] +} + +js_library("viewer-password-screen") { + deps = [ + "//ui/webui/resources/cr_elements/cr_input:cr_input", + ] +} + +js_library("viewer-pdf-toolbar") { + deps = [ + ":viewer-bookmark", + ":viewer-page-selector", + ":viewer-toolbar-dropdown", + "..:annotation_tool", + ] + externs_list = [ "$externs_path/pending.js" ] +} + +js_library("viewer-pen-options") { + externs_list = [ "$externs_path/pending_polymer.js" ] +} + +js_library("viewer-toolbar-dropdown") { + deps = [] +} + +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 = [] +} + +polymer_modulizer("shared-vars") { + js_file = "shared-vars.m.js" + html_file = "shared-vars.html" + html_type = "custom-style" +} + +group("polymer3_elements") { + deps = [ + ":shared-vars_module", + ] +} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.html index 004aa1b1677..004aa1b1677 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.js index f1cce48abaa..f25ebcef472 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.js @@ -7,8 +7,9 @@ * * The bookmark may point at a location in the PDF or a URI. * If it points at a location, |page| indicates which 0-based page it leads to. - * Optionally, |y| is the y position in that page, in pixel coordinates. - * If it points at an URI, |uri| is the target for that bookmark. + * Optionally, |x| is the x position in that page, |y| is the y position in that + * page, in pixel coordinates and |zoom| is the new zoom value. If it points at + * an URI, |uri| is the target for that bookmark. * * |children| is an array of the |Bookmark|s that are below this in a table of * contents tree @@ -16,7 +17,9 @@ * @typedef {{ * title: string, * page: (number | undefined), + * x: (number | undefined), * y: (number | undefined), + * zoom: (number | undefined), * uri: (string | undefined), * children: !Array<!Bookmark> * }} @@ -80,11 +83,15 @@ Polymer({ /** @private */ onClick_: function() { - if (this.bookmark.hasOwnProperty('page')) { - if (this.bookmark.hasOwnProperty('y')) { + if (this.bookmark.page != null) { + if (this.bookmark.zoom != null) { + this.fire('change-zoom', {zoom: this.bookmark.zoom}); + } + if (this.bookmark.x != null && + this.bookmark.y != null) { this.fire('change-page-and-xy', { page: this.bookmark.page, - x: 0, + x: this.bookmark.x, y: this.bookmark.y, origin: 'bookmark' }); @@ -92,7 +99,7 @@ Polymer({ this.fire( 'change-page', {page: this.bookmark.page, origin: 'bookmark'}); } - } else if (this.bookmark.hasOwnProperty('uri')) { + } else if (this.bookmark.uri != null) { this.fire('navigate', {uri: this.bookmark.uri, newtab: true}); } }, diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/BUILD.gn deleted file mode 100644 index fa2b15b86f7..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/BUILD.gn +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2018 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-bookmark", - ] -} - -js_library("viewer-bookmark") { - deps = [ - "//third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior:iron-a11y-keys-behavior-extracted", - ] -} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.html index 25737d45127..25737d45127 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.js b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.js index 082220a4c92..082220a4c92 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/BUILD.gn deleted file mode 100644 index 3beeb5d21b0..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/BUILD.gn +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2018 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-error-screen", - ] -} - -js_library("viewer-error-screen") { - deps = [ - "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", - ] -} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.html index 1e9e1ee4f67..1e9e1ee4f67 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.js index 539eddd6b67..539eddd6b67 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn deleted file mode 100644 index e27ead67627..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn +++ /dev/null @@ -1,18 +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. - -import("//third_party/closure_compiler/compile_js.gni") - -js_type_check("closure_compile") { - deps = [ - ":viewer-form-warning", - ] -} - -js_library("viewer-form-warning") { - deps = [ - "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", - "//ui/webui/resources/js:promise_resolver", - ] -} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.html index 1a7a2ff73ea..1a7a2ff73ea 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.html 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.js index 285edf8a35d..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.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn deleted file mode 100644 index 32ca2b04311..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2018 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-ink-host", - ] -} - -js_library("viewer-ink-host") { - deps = [ - "//chrome/browser/resources/pdf:metrics", - "//chrome/browser/resources/pdf:viewport", - "//chrome/browser/resources/pdf/ink:ink_api", - ] -} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.html index 70ad2578685..70ad2578685 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.js index 50ad4f4cb49..50ad4f4cb49 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/BUILD.gn deleted file mode 100644 index aa1906c65e5..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/BUILD.gn +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2018 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-page-indicator", - ] -} - -js_library("viewer-page-indicator") { - deps = [ - "//ui/webui/resources/js:assert", - "//ui/webui/resources/js:util", - ] -} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.html index e1fdc869efe..e1fdc869efe 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.js b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.js index b7999237a98..b7999237a98 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/BUILD.gn deleted file mode 100644 index 725e3f96591..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/BUILD.gn +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2018 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-page-selector", - ] -} - -js_library("viewer-page-selector") { - deps = [ - "//ui/webui/resources/cr_elements/cr_input:cr_input", - ] -} diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.html b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.html index aebc16bf138..aebc16bf138 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.js b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.js index 5506ad2915c..5506ad2915c 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/BUILD.gn deleted file mode 100644 index 5078ae7403f..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/BUILD.gn +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2018 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-password-screen", - ] -} - -js_library("viewer-password-screen") { - deps = [ - "//ui/webui/resources/cr_elements/cr_input:cr_input", - ] -} 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.html index e183debc86d..348e2215fed 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.html @@ -4,13 +4,13 @@ <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> -<link rel="import" href="../icons.html"> -<link rel="import" href="../viewer-bookmark/viewer-bookmark.html"> -<link rel="import" href="../viewer-page-selector/viewer-page-selector.html"> +<link rel="import" href="icons.html"> +<link rel="import" href="viewer-bookmark.html"> +<link rel="import" href="viewer-page-selector.html"> <if expr="chromeos"> -<link rel="import" href="../viewer-pen-options/viewer-pen-options.html"> +<link rel="import" href="viewer-pen-options.html"> </if> -<link rel="import" href="../viewer-toolbar-dropdown/viewer-toolbar-dropdown.html"> +<link rel="import" href="viewer-toolbar-dropdown.html"> <dom-module id="viewer-pdf-toolbar"> <template> 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.js index afb87dfe6b7..07407cfee75 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.js @@ -1,6 +1,7 @@ // 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. + (function() { Polymer({ is: 'viewer-pdf-toolbar', @@ -24,7 +25,7 @@ Polymer({ reflectToAttribute: true, }, - /** @type {?Object} */ + /** @type {?AnnotationTool} */ annotationTool: { type: Object, value: null, 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 deleted file mode 100644 index fdc650f55b9..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/BUILD.gn +++ /dev/null @@ -1,20 +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. - -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-pen-options/viewer-pen-options.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.html index dbf18b0bfda..dbf18b0bfda 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.html diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.js index 3970e0ff630..d63343404fb 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.js @@ -81,7 +81,7 @@ Polymer({ strings: Object, }, - /** @type {Array<Animation>} */ + /** @type {Array<!Animation>} */ expandAnimations_: null, /** @param {Event} e */ @@ -103,7 +103,8 @@ Polymer({ /** @private */ updateExpandedStateAndFinishAnimations_: function() { this.updateExpandedState_(); - for (const animation of this.expandAnimations_) { + for (const animation of /** @type {!Array<!Animation>} */ ( + this.expandAnimations_)) { animation.finish(); } }, diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/BUILD.gn b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/BUILD.gn deleted file mode 100644 index b542932ff9c..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/BUILD.gn +++ /dev/null @@ -1,14 +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. - -import("//third_party/closure_compiler/compile_js.gni") - -js_type_check("closure_compile") { - deps = [ - ":viewer-pen-options", - ] -} - -js_library("viewer-pen-options") { -} 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.html index 1ebf0df604f..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.html 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.js index 00b2b676b45..191f4cfcfbf 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.js @@ -216,5 +216,4 @@ Polymer({ }); } }); - })(); 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 deleted file mode 100644 index 7a2ca5ce91a..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/BUILD.gn +++ /dev/null @@ -1,15 +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. - -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-zoom-toolbar/viewer-zoom-button.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.html index 000d098cfb3..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-button.html 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-button.js index 2359900d83c..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-button.js diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html index e0f2bdd15ba..0807c5dfe64 100644 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html @@ -1,7 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> -<link rel="import" href="../icons.html"> +<link rel="import" href="icons.html"> <link rel="import" href="viewer-zoom-button.html"> <dom-module id="viewer-zoom-toolbar"> 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.js index fe24ab64645..fe28ef8c96a 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.js @@ -2,6 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * @typedef {{ + * fittingType: !FittingType, + * userInitiated: boolean, + * }} + */ +let FitToChangedEvent; + (function() { const FIT_TO_PAGE_BUTTON_STATE = 0; 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 deleted file mode 100644 index 4c75971c261..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/BUILD.gn +++ /dev/null @@ -1,25 +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. - -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/index.html b/chromium/chrome/browser/resources/pdf/index.html index 06d219a6c7e..00d96432fd0 100644 --- a/chromium/chrome/browser/resources/pdf/index.html +++ b/chromium/chrome/browser/resources/pdf/index.html @@ -4,20 +4,20 @@ <meta charset="utf-8"> <script src="chrome://resources/polymer/v1_0/html-imports/html-imports.min.js"> </script> - <link rel="import" href="elements/viewer-error-screen/viewer-error-screen.html"> - <link rel="import" href="elements/viewer-page-indicator/viewer-page-indicator.html"> - <link rel="import" href="elements/viewer-page-selector/viewer-page-selector.html"> - <link rel="import" href="elements/viewer-password-screen/viewer-password-screen.html"> - <link rel="import" href="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html"> - <link rel="import" href="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html"> + <link rel="import" href="elements/viewer-error-screen.html"> + <link rel="import" href="elements/viewer-page-indicator.html"> + <link rel="import" href="elements/viewer-page-selector.html"> + <link rel="import" href="elements/viewer-password-screen.html"> + <link rel="import" href="elements/viewer-pdf-toolbar.html"> + <link rel="import" href="elements/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"> - <link rel="import" href="elements/viewer-form-warning/viewer-form-warning.html"> + <link rel="import" href="elements/viewer-ink-host.html"> + <link rel="import" href="elements/viewer-form-warning.html"> </if> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> @@ -58,6 +58,7 @@ <script src="chrome://resources/js/promise_resolver.js"></script> <script src="browser_api.js"></script> <script src="metrics.js"></script> +<script src="controller.js"></script> <script src="pdf_viewer.js"></script> <script src="main.js"></script> </html> diff --git a/chromium/chrome/browser/resources/pdf/ink/BUILD.gn b/chromium/chrome/browser/resources/pdf/ink/BUILD.gn index 38908ee25a1..b1c2be215bd 100644 --- a/chromium/chrome/browser/resources/pdf/ink/BUILD.gn +++ b/chromium/chrome/browser/resources/pdf/ink/BUILD.gn @@ -11,6 +11,9 @@ js_type_check("closure_compile") { } js_library("ink_api") { + deps = [ + "..:annotation_tool", + ] externs_list = [ "//third_party/ink/build/ink_lib_externs.js", "$externs_path/pending.js", diff --git a/chromium/chrome/browser/resources/pdf/ink/ink_api.js b/chromium/chrome/browser/resources/pdf/ink/ink_api.js index 04e0d56dcf6..99eed15d900 100644 --- a/chromium/chrome/browser/resources/pdf/ink/ink_api.js +++ b/chromium/chrome/browser/resources/pdf/ink/ink_api.js @@ -4,15 +4,6 @@ /** * @typedef {{ - * tool: string, - * size: number, - * color: string, - * }} - */ -let AnnotationTool; - -/** - * @typedef {{ * canUndo: boolean, * canRedo: boolean, * }} @@ -88,7 +79,7 @@ class InkAPI { }[tool.tool]; this.brush_.setShape(shape); if (tool.tool != 'eraser') { - this.brush_.setColor(tool.color); + this.brush_.setColor(/** @type {string} */ (tool.color)); } this.brush_.setStrokeWidth(tool.size); } 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 d4a502a135f..c33c46b6d75 100644 --- a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js +++ b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js @@ -10,15 +10,15 @@ */ class OpenPdfParamsParser { /** - * @param {function(Object)} postMessageCallback + * @param {function(string):void} getNamedDestinationCallback * Function called to fetch information for a named destination. */ - constructor(postMessageCallback) { + constructor(getNamedDestinationCallback) { /** @private {!Array<!Object>} */ this.outstandingRequests_ = []; - /** @private {!function(Object)} */ - this.postMessageCallback_ = postMessageCallback; + /** @private {!function(string):void} */ + this.getNamedDestinationCallback_ = getNamedDestinationCallback; } /** @@ -181,10 +181,7 @@ class OpenPdfParamsParser { if (params.page === undefined && 'nameddest' in urlParams) { this.outstandingRequests_.push({callback: callback, params: params}); - this.postMessageCallback_({ - type: 'getNamedDestination', - namedDestination: urlParams['nameddest'] - }); + this.getNamedDestinationCallback_(urlParams['nameddest']); } else { callback(params); } diff --git a/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js b/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js index 0ab77f41f3f..5957e13c178 100644 --- a/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js +++ b/chromium/chrome/browser/resources/pdf/pdf_scripting_api.js @@ -6,7 +6,7 @@ * Turn a dictionary received from postMessage into a key event. * * @param {Object} dict A dictionary representing the key event. - * @return {Event} A key event. + * @return {!Event} A key event. */ function DeserializeKeyEvent(dict) { const e = document.createEvent('Event'); @@ -53,66 +53,83 @@ const LoadState = { * Create a new PDFScriptingAPI. This provides a scripting interface to * the PDF viewer so that it can be customized by things like print preview. * - * @param {Window} window the window of the page containing the pdf viewer. - * @param {Object} plugin the plugin element containing the pdf viewer. - * @constructor */ -function PDFScriptingAPI(window, plugin) { - this.loadState_ = LoadState.LOADING; - this.pendingScriptingMessages_ = []; - this.setPlugin(plugin); +class PDFScriptingAPI { + /** + * @param {Window} window the window of the page containing the pdf viewer. + * @param {Object} plugin the plugin element containing the pdf viewer. + */ + constructor(window, plugin) { + this.loadState_ = LoadState.LOADING; + this.pendingScriptingMessages_ = []; + this.setPlugin(plugin); - window.addEventListener('message', event => { - if (event.origin != 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai' && - event.origin != 'chrome://print') { - console.error( - 'Received message that was not from the extension: ' + event); - return; - } - switch (event.data.type) { - case 'viewport': - /** - * @type {{ - * pageX: number, - * pageY: number, - * pageWidth: number, - * viewportWidth: number, - * viewportHeight: number - * }} - */ - const viewportData = event.data; - if (this.viewportChangedCallback_) { - this.viewportChangedCallback_( - viewportData.pageX, viewportData.pageY, viewportData.pageWidth, - viewportData.viewportWidth, viewportData.viewportHeight); - } - break; - case 'documentLoaded': { - const data = /** @type {{load_state: LoadState}} */ (event.data); - this.loadState_ = data.load_state; - if (this.loadCallback_) { - this.loadCallback_(this.loadState_ == LoadState.SUCCESS); - } - break; + /** @private {Function} */ + this.viewportChangedCallback_; + + /** @private {Function} */ + this.loadCallback_; + + /** @private {Function} */ + this.selectedTextCallback_; + + /** @private {Function} */ + this.keyEventCallback_; + + /** @private {Object} */ + this.plugin_; + + window.addEventListener('message', event => { + if (event.origin != + 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai' && + event.origin != 'chrome://print') { + console.error( + 'Received message that was not from the extension: ' + event); + return; } - case 'getSelectedTextReply': { - const data = /** @type {{selectedText: string}} */ (event.data); - if (this.selectedTextCallback_) { - this.selectedTextCallback_(data.selectedText); - this.selectedTextCallback_ = null; + switch (event.data.type) { + case 'viewport': + /** + * @type {{ + * pageX: number, + * pageY: number, + * pageWidth: number, + * viewportWidth: number, + * viewportHeight: number + * }} + */ + const viewportData = event.data; + if (this.viewportChangedCallback_) { + this.viewportChangedCallback_( + viewportData.pageX, viewportData.pageY, viewportData.pageWidth, + viewportData.viewportWidth, viewportData.viewportHeight); + } + break; + case 'documentLoaded': { + const data = /** @type {{load_state: LoadState}} */ (event.data); + this.loadState_ = data.load_state; + if (this.loadCallback_) { + this.loadCallback_(this.loadState_ == LoadState.SUCCESS); + } + break; } - break; - } - case 'sendKeyEvent': - if (this.keyEventCallback_) { - this.keyEventCallback_(DeserializeKeyEvent(event.data.keyEvent)); + case 'getSelectedTextReply': { + const data = /** @type {{selectedText: string}} */ (event.data); + if (this.selectedTextCallback_) { + this.selectedTextCallback_(data.selectedText); + this.selectedTextCallback_ = null; + } + break; } - break; - } - }, false); -} + case 'sendKeyEvent': + if (this.keyEventCallback_) { + this.keyEventCallback_(DeserializeKeyEvent(event.data.keyEvent)); + } + break; + } + }, false); + } -PDFScriptingAPI.prototype = { /** * Send a message to the extension. If messages try to get sent before there * is a plugin element set, then we queue them up and send them later (this @@ -121,13 +138,13 @@ PDFScriptingAPI.prototype = { * @param {Object} message The message to send. * @private */ - sendMessage_: function(message) { + sendMessage_(message) { if (this.plugin_) { this.plugin_.postMessage(message, '*'); } else { this.pendingScriptingMessages_.push(message); } - }, + } /** * Sets the plugin element containing the PDF viewer. The element will usually @@ -135,7 +152,7 @@ PDFScriptingAPI.prototype = { * * @param {Object} plugin the plugin element containing the PDF viewer. */ - setPlugin: function(plugin) { + setPlugin(plugin) { this.plugin_ = plugin; if (this.plugin_) { @@ -147,16 +164,16 @@ PDFScriptingAPI.prototype = { this.sendMessage_(this.pendingScriptingMessages_.shift()); } } - }, + } /** * Sets the callback which will be run when the PDF viewport changes. * * @param {Function} callback the callback to be called. */ - setViewportChangedCallback: function(callback) { + setViewportChangedCallback(callback) { this.viewportChangedCallback_ = callback; - }, + } /** * Sets the callback which will be run when the PDF document has finished @@ -164,20 +181,20 @@ PDFScriptingAPI.prototype = { * * @param {Function} callback the callback to be called. */ - setLoadCallback: function(callback) { + setLoadCallback(callback) { this.loadCallback_ = callback; if (this.loadState_ != LoadState.LOADING && this.loadCallback_) { this.loadCallback_(this.loadState_ == LoadState.SUCCESS); } - }, + } /** * Sets a callback that gets run when a key event is fired in the PDF viewer. * @param {Function} callback the callback to be called with a key event. */ - setKeyEventCallback: function(callback) { + setKeyEventCallback(callback) { this.keyEventCallback_ = callback; - }, + } /** * Resets the PDF viewer into print preview mode. @@ -187,7 +204,7 @@ PDFScriptingAPI.prototype = { * @param {Array<number>} pageNumbers an array of the page numbers. * @param {boolean} modifiable whether or not the document is modifiable. */ - resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) { + resetPrintPreviewMode(url, grayscale, pageNumbers, modifiable) { this.loadState_ = LoadState.LOADING; this.sendMessage_({ type: 'resetPrintPreviewMode', @@ -196,14 +213,14 @@ PDFScriptingAPI.prototype = { pageNumbers: pageNumbers, modifiable: modifiable }); - }, + } /** * Hide the toolbars after a delay. */ - hideToolbars: function() { + hideToolbars() { this.sendMessage_({type: 'hideToolbars'}); - }, + } /** * Load a page into the document while in print preview mode. @@ -211,22 +228,22 @@ PDFScriptingAPI.prototype = { * @param {string} url the url of the pdf page to load. * @param {number} index the index of the page to load. */ - loadPreviewPage: function(url, index) { + loadPreviewPage(url, index) { this.sendMessage_({type: 'loadPreviewPage', url: url, index: index}); - }, + } /** @param {boolean} darkMode Whether the page is in dark mode. */ - darkModeChanged: function(darkMode) { + darkModeChanged(darkMode) { this.sendMessage_({type: 'darkModeChanged', darkMode: darkMode}); - }, + } /** * Select all the text in the document. May only be called after document * load. */ - selectAll: function() { + selectAll() { this.sendMessage_({type: 'selectAll'}); - }, + } /** * Get the selected text in the document. The callback will be called with the @@ -236,40 +253,40 @@ PDFScriptingAPI.prototype = { * @return {boolean} true if the function is successful, false if there is an * outstanding request for selected text that has not been answered. */ - getSelectedText: function(callback) { + getSelectedText(callback) { if (this.selectedTextCallback_) { return false; } this.selectedTextCallback_ = callback; this.sendMessage_({type: 'getSelectedText'}); return true; - }, + } /** * Print the document. May only be called after document load. */ - print: function() { + print() { this.sendMessage_({type: 'print'}); - }, + } /** * Send a key event to the extension. * * @param {Event} keyEvent the key event to send to the extension. */ - sendKeyEvent: function(keyEvent) { + sendKeyEvent(keyEvent) { this.sendMessage_( {type: 'sendKeyEvent', keyEvent: SerializeKeyEvent(keyEvent)}); - }, + } /** * @param {number} scrollX The amount to horizontally scroll in pixels. * @param {number} scrollY The amount to vertically scroll in pixels. */ - scrollPosition: function(scrollX, scrollY) { + scrollPosition(scrollX, scrollY) { this.sendMessage_({type: 'scrollPosition', x: scrollX, y: scrollY}); - }, -}; + } +} /** * Creates a PDF viewer with a scripting interface. This is basically 1) an diff --git a/chromium/chrome/browser/resources/pdf/pdf_viewer.js b/chromium/chrome/browser/resources/pdf/pdf_viewer.js index 375663f029c..d85d80a2101 100644 --- a/chromium/chrome/browser/resources/pdf/pdf_viewer.js +++ b/chromium/chrome/browser/resources/pdf/pdf_viewer.js @@ -6,16 +6,64 @@ /** * @typedef {{ - * dataToSave: Array, - * token: string, - * fileName: string + * source: Object, + * origin: string, + * data: !MessageData, * }} */ -let SaveDataMessageData; +let MessageObject; /** - * @return {number} Width of a scrollbar in pixels + * @typedef {{ + * type: string, + * height: number, + * width: number, + * layoutOptions: (!LayoutOptions|undefined), + * pageDimensions: Array + * }} + */ +let DocumentDimensionsMessageData; + +/** + * @typedef {{ + * type: string, + * url: string, + * disposition: !PdfNavigator.WindowOpenDisposition, + * }} + */ +let NavigateMessageData; + +/** + * @typedef {{ + * type: string, + * page: number, + * x: number, + * y: number, + * zoom: number + * }} + */ +let DestinationMessageData; + +/** + * @typedef {{ + * type: string, + * title: string, + * bookmarks: !Array<!Bookmark>, + * canSerializeDocument: boolean, + * }} */ +let MetadataMessageData; + +/** + * @typedef {{ + * hasUnsavedChanges: (boolean|undefined), + * fileName: string, + * dataToSave: !ArrayBuffer + * }} + */ +let RequiredSaveResult; + +/** @return {number} Width of a scrollbar in pixels */ function getScrollbarWidth() { const div = document.createElement('div'); div.style.visibility = 'hidden'; @@ -31,7 +79,6 @@ function getScrollbarWidth() { /** * Return the filename component of a URL, percent decoded if possible. - * * @param {string} url The URL to get the filename from. * @return {string} The filename component. */ @@ -53,8 +100,7 @@ function getFilenameFromURL(url) { /** * Whether keydown events should currently be ignored. Events are ignored when * an editable element has focus, to allow for proper editing controls. - * - * @param {HTMLElement} activeElement The currently selected DOM node. + * @param {Element} activeElement The currently selected DOM node. * @return {boolean} True if keydown events should be ignored. */ function shouldIgnoreKeyEvents(activeElement) { @@ -70,285 +116,341 @@ function shouldIgnoreKeyEvents(activeElement) { } /** - * Creates a cryptographically secure pseudorandom 128-bit token. - * - * @return {string} The generated token as a hex string. + * Creates a new PDFViewer. There should only be one of these objects per + * document. */ -function createToken() { - const randomBytes = new Uint8Array(16); - return window.crypto.getRandomValues(randomBytes) - .map(b => b.toString(16).padStart(2, '0')) - .join(''); -} +class PDFViewer { + /** + * @param {!BrowserApi} browserApi An object providing an API to the browser. + */ + constructor(browserApi) { + /** @private {!BrowserApi} */ + this.browserApi_ = browserApi; -/** - * The minimum number of pixels to offset the toolbar by from the bottom and - * right side of the screen. - */ -PDFViewer.MIN_TOOLBAR_OFFSET = 15; + /** @private {string} */ + this.originalUrl_ = this.browserApi_.getStreamInfo().originalUrl; -/** - * The height of the toolbar along the top of the page. The document will be - * shifted down by this much in the viewport. - */ -PDFViewer.MATERIAL_TOOLBAR_HEIGHT = 56; + /** @private {string} */ + this.javascript_ = this.browserApi_.getStreamInfo().javascript || 'block'; -/** - * Minimum height for the material toolbar to show (px). Should match the media - * query in index-material.css. If the window is smaller than this at load, - * leave no space for the toolbar. - */ -PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT = 250; + /** @private {!LoadState} */ + this.loadState_ = LoadState.LOADING; -/** - * The background color used for print preview (--google-grey-refresh-300). - */ -PDFViewer.PRINT_PREVIEW_BACKGROUND_COLOR = '0xFFDADCE0'; + /** @private {?Object} */ + this.parentWindow_ = null; -/** - * The background color used for print preview when dark mode is enabled - * (--google-grey-refresh-700). - */ -PDFViewer.PRINT_PREVIEW_DARK_BACKGROUND_COLOR = '0xFF5F6368'; + /** @private {?string} */ + this.parentOrigin_ = null; -/** - * The background color used for the regular viewer. - */ -PDFViewer.BACKGROUND_COLOR = '0xFF525659'; + /** @private {boolean} */ + this.isFormFieldFocused_ = false; -/** - * Creates a new PDFViewer. There should only be one of these objects per - * document. - * - * @param {!BrowserApi} browserApi An object providing an API to the browser. - * @constructor - */ -function PDFViewer(browserApi) { - this.browserApi_ = browserApi; - this.originalUrl_ = this.browserApi_.getStreamInfo().originalUrl; - this.javascript_ = this.browserApi_.getStreamInfo().javascript || 'block'; - this.loadState_ = LoadState.LOADING; - this.parentWindow_ = null; - this.parentOrigin_ = null; - this.isFormFieldFocused_ = false; - this.beepCount_ = 0; - this.delayedScriptingMessages_ = []; - this.loaded_ = new PromiseResolver(); - - this.isPrintPreview_ = location.origin === 'chrome://print'; - this.isPrintPreviewLoadingFinished_ = false; - this.isUserInitiatedEvent_ = true; - - /** @private {boolean} */ - this.hasEnteredAnnotationMode_ = false; - - /** @private {boolean} */ - this.hadPassword_ = false; - - /** @private {boolean} */ - this.canSerializeDocument_ = false; - - PDFMetrics.record(PDFMetrics.UserAction.DOCUMENT_OPENED); - - // Parse open pdf parameters. - this.paramsParser_ = new OpenPdfParamsParser( - message => this.pluginController_.postMessage(message)); - const toolbarEnabled = - this.paramsParser_.getUiUrlParams(this.originalUrl_).toolbar && - !this.isPrintPreview_; - - // The sizer element is placed behind the plugin element to cause scrollbars - // to be displayed in the window. It is sized according to the document size - // of the pdf and zoom level. - this.sizer_ = $('sizer'); - - if (this.isPrintPreview_) { - this.pageIndicator_ = $('page-indicator'); - } - this.passwordScreen_ = $('password-screen'); - this.passwordScreen_.addEventListener( - '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) { - this.errorScreen_.reloadFn = () => { - chrome.tabs.reload(this.browserApi_.getStreamInfo().tabId); - }; - } + /** @private {number} */ + this.beepCount_ = 0; - // Create the viewport. - const shortWindow = window.innerHeight < PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT; - const topToolbarHeight = - (toolbarEnabled) ? PDFViewer.MATERIAL_TOOLBAR_HEIGHT : 0; - const defaultZoom = - this.browserApi_.getZoomBehavior() == BrowserApi.ZoomBehavior.MANAGE ? - this.browserApi_.getDefaultZoom() : - 1.0; - 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 - // positioning, acting as a viewport. The plugin renders into this viewport - // according to the scroll position of the window. - this.plugin_ = document.createElement('embed'); - // NOTE: The plugin's 'id' field must be set to 'plugin' since - // chrome/renderer/printing/print_render_frame_helper.cc actually - // references it. - this.plugin_.id = 'plugin'; - this.plugin_.type = 'application/x-google-chrome-pdf'; - - // Handle scripting messages from outside the extension that wish to interact - // with it. We also send a message indicating that extension has loaded and - // is ready to receive messages. - window.addEventListener( - 'message', message => this.handleScriptingMessage(message), false); - - this.plugin_.setAttribute('src', this.originalUrl_); - this.plugin_.setAttribute( - 'stream-url', this.browserApi_.getStreamInfo().streamUrl); - let headers = ''; - for (const header in this.browserApi_.getStreamInfo().responseHeaders) { - headers += header + ': ' + - this.browserApi_.getStreamInfo().responseHeaders[header] + '\n'; - } - this.plugin_.setAttribute('headers', headers); + /** @private {!Array} */ + this.delayedScriptingMessages_ = []; + + /** @private {!PromiseResolver} */ + this.loaded_; + + /** @private {boolean} */ + this.initialLoadComplete_ = false; + + /** @private {boolean} */ + this.isPrintPreview_ = location.origin === 'chrome://print'; - this.plugin_.setAttribute('background-color', PDFViewer.BACKGROUND_COLOR); - this.plugin_.setAttribute('top-toolbar-height', topToolbarHeight); - this.plugin_.setAttribute('javascript', this.javascript_); + /** @private {boolean} */ + this.isPrintPreviewLoadingFinished_ = false; - if (this.browserApi_.getStreamInfo().embedded) { + /** @private {boolean} */ + this.isUserInitiatedEvent_ = true; + + /** @private {boolean} */ + this.hasEnteredAnnotationMode_ = false; + + /** @private {boolean} */ + this.hadPassword_ = false; + + /** @private {boolean} */ + this.canSerializeDocument_ = false; + + /** @private {!EventTracker} */ + this.tracker_ = new EventTracker(); + + PDFMetrics.record(PDFMetrics.UserAction.DOCUMENT_OPENED); + + // Parse open pdf parameters. + /** @private {!OpenPdfParamsParser} */ + this.paramsParser_ = new OpenPdfParamsParser( + destination => this.pluginController_.getNamedDestination(destination)); + const toolbarEnabled = + this.paramsParser_.getUiUrlParams(this.originalUrl_).toolbar && + !this.isPrintPreview_; + + // The sizer element is placed behind the plugin element to cause scrollbars + // to be displayed in the window. It is sized according to the document size + // of the pdf and zoom level. + this.sizer_ = /** @type {!HTMLDivElement} */ ($('sizer')); + + /** @private {?ViewerPageIndicatorElement} */ + this.pageIndicator_ = this.isPrintPreview_ ? + /** @type {!ViewerPageIndicatorElement} */ ($('page-indicator')) : + null; + + /** @private {?ViewerPasswordScreenElement} */ + this.passwordScreen_ = + /** @type {!ViewerPasswordScreenElement} */ ($('password-screen')); + this.passwordScreen_.addEventListener('password-submitted', e => { + this.onPasswordSubmitted_( + /** @type {!CustomEvent<{password: string}>} */ (e)); + }); + + /** @private {?ViewerErrorScreenElement} */ + this.errorScreen_ = + /** @type {!ViewerErrorScreenElement} */ ($('error-screen')); + // Can only reload if we are in a normal tab. + if (chrome.tabs && this.browserApi_.getStreamInfo().tabId != -1) { + this.errorScreen_.reloadFn = () => { + chrome.tabs.reload(this.browserApi_.getStreamInfo().tabId); + }; + } + + // Create the viewport. + const shortWindow = + window.innerHeight < PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT; + const topToolbarHeight = + (toolbarEnabled) ? PDFViewer.MATERIAL_TOOLBAR_HEIGHT : 0; + const defaultZoom = + this.browserApi_.getZoomBehavior() == BrowserApi.ZoomBehavior.MANAGE ? + this.browserApi_.getDefaultZoom() : + 1.0; + + /** @private {!Viewport} */ + 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.resetTrackers_()); + + // Create the plugin object dynamically so we can set its src. The plugin + // element is sized to fill the entire window and is set to be fixed + // positioning, acting as a viewport. The plugin renders into this viewport + // according to the scroll position of the window. + /** @private {!HTMLEmbedElement} */ + this.plugin_ = + /** @type {!HTMLEmbedElement} */ (document.createElement('embed')); + + // NOTE: The plugin's 'id' field must be set to 'plugin' since + // chrome/renderer/printing/print_render_frame_helper.cc actually + // references it. + this.plugin_.id = 'plugin'; + this.plugin_.type = 'application/x-google-chrome-pdf'; + + // Handle scripting messages from outside the extension that wish to + // interact with it. We also send a message indicating that extension has + // loaded and is ready to receive messages. + window.addEventListener('message', message => { + this.handleScriptingMessage(/** @type {!MessageObject} */ (message)); + }, false); + + this.plugin_.setAttribute('src', this.originalUrl_); this.plugin_.setAttribute( - 'top-level-url', this.browserApi_.getStreamInfo().tabUrl); - } else { - this.plugin_.setAttribute('full-frame', ''); - } + 'stream-url', this.browserApi_.getStreamInfo().streamUrl); + let headers = ''; + for (const header in this.browserApi_.getStreamInfo().responseHeaders) { + headers += header + ': ' + + this.browserApi_.getStreamInfo().responseHeaders[header] + '\n'; + } + this.plugin_.setAttribute('headers', headers); - $('content').appendChild(this.plugin_); - - this.pluginController_ = - new PluginController(this.plugin_, this, this.viewport_); - this.inkController_ = new InkController(this, this.viewport_); - this.currentController_ = this.pluginController_; - - // Setup the button event listeners. - this.zoomToolbar_ = $('zoom-toolbar'); - this.zoomToolbar_.setIsPrintPreview(this.isPrintPreview_); - this.zoomToolbar_.addEventListener( - 'fit-to-changed', e => this.fitToChanged_(e)); - this.zoomToolbar_.addEventListener('zoom-in', () => this.viewport_.zoomIn()); - this.zoomToolbar_.addEventListener( - 'zoom-out', () => this.viewport_.zoomOut()); - - this.gestureDetector_ = new GestureDetector($('content')); - this.gestureDetector_.addEventListener( - 'pinchstart', e => this.onPinchStart_(e)); - this.sentPinchEvent_ = false; - this.gestureDetector_.addEventListener( - 'pinchupdate', e => this.onPinchUpdate_(e)); - this.gestureDetector_.addEventListener('pinchend', e => this.onPinchEnd_(e)); - - if (toolbarEnabled) { - this.toolbar_ = $('toolbar'); - this.toolbar_.hidden = false; - this.toolbar_.addEventListener('save', () => this.save()); - this.toolbar_.addEventListener('print', () => this.print()); - this.toolbar_.addEventListener( - 'undo', () => this.currentController_.undo()); - this.toolbar_.addEventListener( - 'redo', () => this.currentController_.redo()); - this.toolbar_.addEventListener( - 'rotate-right', () => this.rotateClockwise()); - this.toolbar_.addEventListener( - 'annotation-mode-toggled', e => this.annotationModeToggled_(e)); - this.toolbar_.addEventListener( - 'annotation-tool-changed', - e => this.inkController_.setAnnotationTool(e.detail.value)); - - this.toolbar_.docTitle = getFilenameFromURL(this.originalUrl_); - } + this.plugin_.setAttribute('background-color', PDFViewer.BACKGROUND_COLOR); + this.plugin_.setAttribute('top-toolbar-height', topToolbarHeight); + this.plugin_.setAttribute('javascript', this.javascript_); - document.body.addEventListener('change-page', e => { - this.viewport_.goToPage(e.detail.page); - if (e.detail.origin == 'bookmark') { - PDFMetrics.record(PDFMetrics.UserAction.FOLLOW_BOOKMARK); - } else if (e.detail.origin == 'pageselector') { - PDFMetrics.record(PDFMetrics.UserAction.PAGE_SELECTOR_NAVIGATE); + if (this.browserApi_.getStreamInfo().embedded) { + this.plugin_.setAttribute( + 'top-level-url', this.browserApi_.getStreamInfo().tabUrl); + } else { + this.plugin_.setAttribute('full-frame', ''); } - }); - - document.body.addEventListener('change-page-and-xy', e => { - const point = this.viewport_.convertPageToScreen(e.detail.page, e.detail); - this.goToPageAndXY_(e.detail.origin, e.detail.page, point); - }); - - document.body.addEventListener('navigate', e => { - const disposition = e.detail.newtab ? - PdfNavigator.WindowOpenDisposition.NEW_BACKGROUND_TAB : - PdfNavigator.WindowOpenDisposition.CURRENT_TAB; - this.navigator_.navigate(e.detail.uri, disposition); - }); - - document.body.addEventListener('dropdown-opened', e => { - if (e.detail == 'bookmarks') { - PDFMetrics.record(PDFMetrics.UserAction.OPEN_BOOKMARKS_PANEL); + + $('content').appendChild(this.plugin_); + + /** @private {!PluginController} */ + this.pluginController_ = new PluginController( + this.plugin_, this.viewport_, () => this.isUserInitiatedEvent_, + () => this.loaded); + this.tracker_.add( + this.pluginController_.getEventTarget(), 'plugin-message', + e => this.handlePluginMessage_(e)); + + /** @private {!InkController} */ + this.inkController_ = new InkController(this.viewport_); + this.tracker_.add( + this.inkController_.getEventTarget(), 'stroke-added', + () => chrome.mimeHandlerPrivate.setShowBeforeUnloadDialog(true)); + this.tracker_.add( + this.inkController_.getEventTarget(), 'set-annotation-undo-state', + e => this.setAnnotationUndoState_(e)); + + /** @private {!ContentController} */ + this.currentController_ = this.pluginController_; + + // Setup the button event listeners. + /** @private {!ViewerZoomToolbarElement} */ + this.zoomToolbar_ = + /** @type {!ViewerZoomToolbarElement} */ ($('zoom-toolbar')); + this.zoomToolbar_.setIsPrintPreview(this.isPrintPreview_); + this.zoomToolbar_.addEventListener( + 'fit-to-changed', + e => this.fitToChanged_( + /** @type {!CustomEvent<FitToChangedEvent>}} */ (e))); + this.zoomToolbar_.addEventListener( + 'zoom-in', () => this.viewport_.zoomIn()); + this.zoomToolbar_.addEventListener( + 'zoom-out', () => this.viewport_.zoomOut()); + + /** @private {!GestureDetector} */ + this.gestureDetector_ = new GestureDetector(assert($('content'))); + this.gestureDetector_.addEventListener( + 'pinchstart', e => this.onPinchStart_(e)); + this.sentPinchEvent_ = false; + this.gestureDetector_.addEventListener( + 'pinchupdate', e => this.onPinchUpdate_(e)); + this.gestureDetector_.addEventListener( + 'pinchend', e => this.onPinchEnd_(e)); + + /** @private {?ViewerPdfToolbarElement} */ + this.toolbar_ = null; + if (toolbarEnabled) { + this.toolbar_ = /** @type {!ViewerPdfToolbarElement} */ ($('toolbar')); + this.toolbar_.hidden = false; + this.toolbar_.addEventListener('save', () => this.save_()); + this.toolbar_.addEventListener('print', () => this.print_()); + this.toolbar_.addEventListener( + 'undo', () => this.currentController_.undo()); + this.toolbar_.addEventListener( + 'redo', () => this.currentController_.redo()); + this.toolbar_.addEventListener( + 'rotate-right', () => this.rotateClockwise_()); + this.toolbar_.addEventListener('annotation-mode-toggled', e => { + this.annotationModeToggled_( + /** @type {!CustomEvent<{value: boolean}>} */ (e)); + }); + this.toolbar_.addEventListener( + 'annotation-tool-changed', + e => this.inkController_.setAnnotationTool(e.detail.value)); + + this.toolbar_.docTitle = getFilenameFromURL(this.originalUrl_); + } + + document.body.addEventListener('change-page', e => { + this.viewport_.goToPage(e.detail.page); + if (e.detail.origin == 'bookmark') { + PDFMetrics.record(PDFMetrics.UserAction.FOLLOW_BOOKMARK); + } else if (e.detail.origin == 'pageselector') { + PDFMetrics.record(PDFMetrics.UserAction.PAGE_SELECTOR_NAVIGATE); + } + }); + + document.body.addEventListener('change-zoom', e => { + this.viewport_.setZoom(e.detail.zoom); + }); + + document.body.addEventListener('change-page-and-xy', e => { + const point = this.viewport_.convertPageToScreen(e.detail.page, e.detail); + this.goToPageAndXY_(e.detail.origin, e.detail.page, point); + }); + + document.body.addEventListener('navigate', e => { + const disposition = e.detail.newtab ? + PdfNavigator.WindowOpenDisposition.NEW_BACKGROUND_TAB : + PdfNavigator.WindowOpenDisposition.CURRENT_TAB; + this.navigator_.navigate(e.detail.uri, disposition); + }); + + document.body.addEventListener('dropdown-opened', e => { + if (e.detail == 'bookmarks') { + PDFMetrics.record(PDFMetrics.UserAction.OPEN_BOOKMARKS_PANEL); + } + }); + + /** @private {!ToolbarManager} */ + this.toolbarManager_ = + new ToolbarManager(window, this.toolbar_, this.zoomToolbar_); + + // Set up the ZoomManager. + /** @private {!ZoomManager} */ + this.zoomManager_ = ZoomManager.create( + this.browserApi_.getZoomBehavior(), () => this.viewport_.getZoom(), + zoom => this.browserApi_.setZoom(zoom), + this.browserApi_.getInitialZoom()); + this.viewport_.setZoomManager(this.zoomManager_); + this.browserApi_.addZoomEventListener( + zoom => this.zoomManager_.onBrowserZoomChange(zoom)); + + // Setup the keyboard event listener. + document.addEventListener( + 'keydown', + e => this.handleKeyEvent_(/** @type {!KeyboardEvent} */ (e))); + document.addEventListener('mousemove', e => this.handleMouseEvent_(e)); + document.addEventListener('mouseout', e => this.handleMouseEvent_(e)); + document.addEventListener( + 'contextmenu', e => this.handleContextMenuEvent_(e)); + + const tabId = this.browserApi_.getStreamInfo().tabId; + /** @private {!PdfNavigator} */ + this.navigator_ = new PdfNavigator( + this.originalUrl_, this.viewport_, this.paramsParser_, + new NavigatorDelegate(tabId)); + + /** @private {!ViewportScroller} */ + this.viewportScroller_ = + new ViewportScroller(this.viewport_, this.plugin_, window); + + /** @private {!Array<!Bookmark>} */ + this.bookmarks_; + + /** @private {!Point} */ + this.lastViewportPosition_; + + /** @private {boolean} */ + this.reverseZoomToolbar_; + + /** @private {boolean} */ + this.inPrintPreviewMode_; + + /** @private {boolean} */ + this.dark_; + + /** @private {!DocumentDimensionsMessageData} */ + this.documentDimensions_; + + // Request translated strings. + chrome.resourcesPrivate.getStrings( + chrome.resourcesPrivate.Component.PDF, + strings => this.handleStrings_(strings)); + + // Listen for save commands from the browser. + if (chrome.mimeHandlerPrivate && chrome.mimeHandlerPrivate.onSave) { + chrome.mimeHandlerPrivate.onSave.addListener(url => this.onSave_(url)); } - }); - - this.toolbarManager_ = - new ToolbarManager(window, this.toolbar_, this.zoomToolbar_); - - // Set up the ZoomManager. - this.zoomManager_ = ZoomManager.create( - this.browserApi_.getZoomBehavior(), () => this.viewport_.getZoom(), - zoom => this.browserApi_.setZoom(zoom), - this.browserApi_.getInitialZoom()); - this.viewport_.setZoomManager(this.zoomManager_); - this.browserApi_.addZoomEventListener( - zoom => this.zoomManager_.onBrowserZoomChange(zoom)); - - // Setup the keyboard event listener. - document.addEventListener('keydown', e => this.handleKeyEvent_(e)); - document.addEventListener('mousemove', e => this.handleMouseEvent_(e)); - document.addEventListener('mouseout', e => this.handleMouseEvent_(e)); - document.addEventListener( - 'contextmenu', e => this.handleContextMenuEvent_(e)); - - const tabId = this.browserApi_.getStreamInfo().tabId; - 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', strings => this.handleStrings_(strings)); - - // Listen for save commands from the browser. - if (chrome.mimeHandlerPrivate && chrome.mimeHandlerPrivate.onSave) { - chrome.mimeHandlerPrivate.onSave.addListener(url => this.onSave(url)); } -} -PDFViewer.prototype = { /** * Handle key events. These may come from the user directly or via the * scripting API. - * - * @param {KeyboardEvent} e the event to handle. + * @param {!KeyboardEvent} e the event to handle. * @private */ - handleKeyEvent_: function(e) { + handleKeyEvent_(e) { const position = this.viewport_.position; // Certain scroll events may be sent from outside of the extension. const fromScriptingAPI = e.fromScriptingAPI; @@ -357,7 +459,7 @@ PDFViewer.prototype = { return; } - this.toolbarManager_.hideToolbarsAfterTimeout(e); + this.toolbarManager_.hideToolbarsAfterTimeout(); const pageUpHandler = () => { // Go to the previous page if we are fit-to-page or fit-to-height. @@ -366,8 +468,8 @@ PDFViewer.prototype = { // Since we do the movement of the page. e.preventDefault(); } else if (fromScriptingAPI) { - position.y -= this.viewport.size.height; - this.viewport.position = position; + position.y -= this.viewport_.size.height; + this.viewport_.position = position; } }; const pageDownHandler = () => { @@ -377,8 +479,8 @@ PDFViewer.prototype = { // Since we do the movement of the page. e.preventDefault(); } else if (fromScriptingAPI) { - position.y += this.viewport.size.height; - this.viewport.position = position; + position.y += this.viewport_.size.height; + this.viewport_.position = position; } }; @@ -416,14 +518,14 @@ PDFViewer.prototype = { e.preventDefault(); } else if (fromScriptingAPI) { position.x -= Viewport.SCROLL_INCREMENT; - this.viewport.position = position; + this.viewport_.position = position; } } return; case 38: // Up arrow key. if (fromScriptingAPI) { position.y -= Viewport.SCROLL_INCREMENT; - this.viewport.position = position; + this.viewport_.position = position; } return; case 39: // Right arrow key. @@ -437,19 +539,19 @@ PDFViewer.prototype = { e.preventDefault(); } else if (fromScriptingAPI) { position.x += Viewport.SCROLL_INCREMENT; - this.viewport.position = position; + this.viewport_.position = position; } } return; case 40: // Down arrow key. if (fromScriptingAPI) { position.y += Viewport.SCROLL_INCREMENT; - this.viewport.position = position; + this.viewport_.position = position; } return; case 65: // 'a' key. if (e.ctrlKey || e.metaKey) { - this.pluginController_.postMessage({type: 'selectAll'}); + this.pluginController_.selectAll(); // Since we do selection ourselves. e.preventDefault(); } @@ -462,7 +564,7 @@ PDFViewer.prototype = { return; case 219: // Left bracket key. if (e.ctrlKey) { - this.rotateCounterclockwise(); + this.rotateCounterclockwise_(); } return; case 220: // Backslash key. @@ -472,7 +574,7 @@ PDFViewer.prototype = { return; case 221: // Right bracket key. if (e.ctrlKey) { - this.rotateClockwise(); + this.rotateClockwise_(); } return; } @@ -487,42 +589,49 @@ PDFViewer.prototype = { this.toolbarManager_.showToolbars(); } } - }, + } - handleMouseEvent_: function(e) { + handleMouseEvent_(e) { if (e.type == 'mousemove') { this.toolbarManager_.handleMouseMove(e); } else if (e.type == 'mouseout') { this.toolbarManager_.hideToolbarsForMouseOut(); } - }, + } - handleContextMenuEvent_: function(e) { + /** + * @param {!Event} e The context menu event + * @private + */ + handleContextMenuEvent_(e) { // Stop Chrome from popping up the context menu on long press. We need to // make sure the start event did not have 2 touches because we don't want // to block two finger tap opening the context menu. We check for // firesTouchEvents in order to not block the context menu on right click. - if (e.sourceCapabilities.firesTouchEvents && + const capabilities = + /** @type {{ sourceCapabilities: Object }} */ (e).sourceCapabilities; + if (capabilities.firesTouchEvents && !this.gestureDetector_.wasTwoFingerTouch()) { e.preventDefault(); } - }, + } /** * Handles the annotation mode being toggled on or off. - * * @param {!CustomEvent<{value: boolean}>} e * @private */ - annotationModeToggled_: async function(e) { + async annotationModeToggled_(e) { const annotationMode = e.detail.value; if (annotationMode) { // Enter annotation mode. assert(this.currentController_ == this.pluginController_); // TODO(dstockwell): set plugin read-only, begin transition - this.updateProgress(0); + this.updateProgress_(0); // TODO(dstockwell): handle save failure - const result = await this.pluginController_.save(true); + const saveResult = await this.pluginController_.save(true); + // Data always exists when save is called with requireResult = true. + const result = /** @type {!RequiredSaveResult} */ (saveResult); if (result.hasUnsavedChanges) { assert(!loadTimeData.getBoolean('pdfFormSaveEnabled')); try { @@ -530,25 +639,26 @@ PDFViewer.prototype = { } catch (e) { // The user aborted entering annotation mode. Revert to the plugin. this.toolbar_.annotationMode = false; - this.updateProgress(100); + this.updateProgress_(100); return; } } PDFMetrics.record(PDFMetrics.UserAction.ENTER_ANNOTATION_MODE); this.hasEnteredAnnotationMode_ = true; // TODO(dstockwell): feed real progress data from the Ink component - this.updateProgress(50); + this.updateProgress_(50); await this.inkController_.load(result.fileName, result.dataToSave); - this.inkController_.setAnnotationTool(this.toolbar_.annotationTool); + this.inkController_.setAnnotationTool( + assert(this.toolbar_.annotationTool)); this.currentController_ = this.inkController_; this.pluginController_.unload(); - this.updateProgress(100); + this.updateProgress_(100); } else { // Exit annotation mode. PDFMetrics.record(PDFMetrics.UserAction.EXIT_ANNOTATION_MODE); assert(this.currentController_ == this.inkController_); // TODO(dstockwell): set ink read-only, begin transition - this.updateProgress(0); + this.updateProgress_(0); // This runs separately to allow other consumers of `loaded` to queue // up after this task. this.loaded.then(() => { @@ -556,36 +666,33 @@ PDFViewer.prototype = { this.inkController_.unload(); }); // TODO(dstockwell): handle save failure - const result = await this.inkController_.save(true); + const saveResult = await this.inkController_.save(true); + // Data always exists when save is called with requireResult = true. + const result = /** @type {!RequiredSaveResult} */ (saveResult); await this.pluginController_.load(result.fileName, result.dataToSave); // Ensure the plugin gets the initial viewport. this.pluginController_.afterZoom(); } - }, + } /** * Exits annotation mode if active. - * * @return {Promise<void>} */ - exitAnnotationMode_: async function() { + async exitAnnotationMode_() { if (!this.toolbar_.annotationMode) { return; } this.toolbar_.toggleAnnotation(); await this.loaded; - }, + } /** * Request to change the viewport fitting type. - * - * @param {!CustomEvent<{ - * fittingType: FittingType, - * userInitiated: boolean - * }>} e + * @param {!CustomEvent<FitToChangedEvent>} e * @private */ - fitToChanged_: function(e) { + fitToChanged_(e) { if (e.detail.fittingType == FittingType.FIT_TO_PAGE) { this.viewport_.fitToPage(); this.toolbarManager_.forceHideTopToolbar(); @@ -599,15 +706,14 @@ PDFViewer.prototype = { if (e.detail.userInitiated) { PDFMetrics.recordFitTo(e.detail.fittingType); } - }, + } /** * Sends a 'documentLoaded' message to the PDFScriptingAPI if the document has * finished loading. - * * @private */ - sendDocumentLoadedMessage_: function() { + sendDocumentLoadedMessage_() { if (this.loadState_ == LoadState.LOADING) { return; } @@ -616,17 +722,16 @@ PDFViewer.prototype = { } this.sendScriptingMessage_( {type: 'documentLoaded', load_state: this.loadState_}); - }, + } /** * Handle open pdf parameters. This function updates the viewport as per * the parameters mentioned in the url while opening pdf. The order is * important as later actions can override the effects of previous actions. - * * @param {Object} params The open params passed in the URL. * @private */ - handleURLParams_: function(params) { + handleURLParams_(params) { if (params.zoom) { this.viewport_.setZoom(params.zoom); } @@ -654,32 +759,42 @@ PDFViewer.prototype = { } this.isUserInitiatedEvent_ = true; } - }, + } /** * Moves the viewport to a point in a page. Called back after a * 'transformPagePointReply' is returned from the plugin. - * * @param {string} origin Identifier for the caller for logging purposes. * @param {number} page The index of the page to go to. zero-based. - * @param {Object} message Message received from the plugin containing the + * @param {Point} message Message received from the plugin containing the * x and y to navigate to in screen coordinates. * @private */ - goToPageAndXY_: function(origin, page, message) { + goToPageAndXY_(origin, page, message) { this.viewport_.goToPageAndXY(page, message.x, message.y); if (origin == 'bookmark') { PDFMetrics.record(PDFMetrics.UserAction.FOLLOW_BOOKMARK); } - }, + } /** - * @return {Promise} Resolved when the load state reaches LOADED, - * rejects on FAILED. + * @return {?Promise} Resolved when the load state reaches LOADED, + * rejects on FAILED. Returns null if no promise has been created, which + * is the case for initial load of the PDF. */ get loaded() { - return this.loaded_.promise; - }, + return this.loaded_ ? this.loaded_.promise : null; + } + + /** @return {!Viewport} The viewport. Used for testing. */ + get viewport() { + return this.viewport_; + } + + /** @return {!Array<!Bookmark>} The bookmarks. Used for testing. */ + get bookmarks() { + return this.bookmarks_; + } /** * Updates the load state and triggers completion of the `loaded` @@ -691,26 +806,29 @@ PDFViewer.prototype = { if (this.loadState_ == loadState) { return; } + assert( + loadState == LoadState.LOADING || this.loadState_ == LoadState.LOADING); + this.loadState_ = loadState; + if (!this.initialLoadComplete_) { + this.initialLoadComplete_ = true; + return; + } if (loadState == LoadState.SUCCESS) { - assert(this.loadState_ == LoadState.LOADING); this.loaded_.resolve(); } else if (loadState == LoadState.FAILED) { - assert(this.loadState_ == LoadState.LOADING); this.loaded_.reject(); } else { - assert(loadState == LoadState.LOADING); this.loaded_ = new PromiseResolver(); } - this.loadState_ = loadState; - }, + } /** * Update the loading progress of the document in response to a progress * message being received from the content controller. - * * @param {number} progress the progress as a percentage. + * @private */ - updateProgress: function(progress) { + updateProgress_(progress) { if (this.toolbar_) { this.toolbar_.loadProgress = progress; } @@ -743,28 +861,26 @@ PDFViewer.prototype = { } else { this.setLoadState_(LoadState.LOADING); } - }, + } /** @private */ - sendBackgroundColorForPrintPreview_: function() { - this.pluginController_.postMessage({ - type: 'backgroundColorChanged', - backgroundColor: this.dark_ ? - PDFViewer.PRINT_PREVIEW_DARK_BACKGROUND_COLOR : - PDFViewer.PRINT_PREVIEW_BACKGROUND_COLOR, - }); - }, + sendBackgroundColorForPrintPreview_() { + this.pluginController_.backgroundColorChanged( + this.dark_ ? PDFViewer.PRINT_PREVIEW_DARK_BACKGROUND_COLOR : + PDFViewer.PRINT_PREVIEW_BACKGROUND_COLOR); + } /** * Load a dictionary of translated strings into the UI. Used as a callback for * chrome.resourcesPrivate. - * * @param {Object} strings Dictionary of translated strings * @private */ - handleStrings_: function(strings) { - document.documentElement.dir = strings.textdirection; - document.documentElement.lang = strings.language; + handleStrings_(strings) { + const stringsDictionary = + /** @type {{ textdirection: string, language: string }} */ (strings); + document.documentElement.dir = stringsDictionary.textdirection; + document.documentElement.lang = stringsDictionary.language; loadTimeData.data = strings; const isNewPrintPreview = this.isPrintPreview_ && @@ -786,38 +902,34 @@ PDFViewer.prototype = { if ($('form-warning')) { $('form-warning').strings = strings; } - }, + } /** * An event handler for handling password-submitted events. These are fired * when an event is entered into the password screen. - * - * @param {Object} event a password-submitted event. + * @param {!CustomEvent<{password: string}>} event a password-submitted event. * @private */ - onPasswordSubmitted_: function(event) { - this.pluginController_.postMessage( - {type: 'getPasswordComplete', password: event.detail.password}); - }, + onPasswordSubmitted_(event) { + this.pluginController_.getPasswordComplete(event.detail.password); + } /** * A callback that sets |isUserInitiatedEvent_| to |userInitiated|. - * * @param {boolean} userInitiated The value to set |isUserInitiatedEvent_| to. * @private */ - setUserInitiated_: function(userInitiated) { + setUserInitiated_(userInitiated) { assert(this.isUserInitiatedEvent_ != userInitiated); this.isUserInitiatedEvent_ = userInitiated; - }, + } /** * A callback that's called when an update to a pinch zoom is detected. - * * @param {!Object} e the pinch event. * @private */ - onPinchUpdate_: function(e) { + onPinchUpdate_(e) { // Throttle number of pinch events to one per frame. if (!this.sentPinchEvent_) { this.sentPinchEvent_ = true; @@ -826,42 +938,39 @@ PDFViewer.prototype = { this.viewport_.pinchZoom(e); }); } - }, + } /** * A callback that's called when the end of a pinch zoom is detected. - * * @param {!Object} e the pinch event. * @private */ - onPinchEnd_: function(e) { + onPinchEnd_(e) { // Using rAF for pinch end prevents pinch updates scheduled by rAF getting // sent after the pinch end. window.requestAnimationFrame(() => { this.viewport_.pinchZoomEnd(e); }); - }, + } /** * A callback that's called when the start of a pinch zoom is detected. - * * @param {!Object} e the pinch event. * @private */ - onPinchStart_: function(e) { + onPinchStart_(e) { // We also use rAF for pinch start, so that if there is a pinch end event // scheduled by rAF, this pinch start will be sent after. window.requestAnimationFrame(() => { this.viewport_.pinchZoomStart(e); }); - }, + } /** * A callback that's called after the viewport changes. - * * @private */ - viewportChanged_: function() { + viewportChanged_() { if (!this.documentDimensions_) { return; } @@ -900,9 +1009,10 @@ PDFViewer.prototype = { // TODO(raymes): Give pageIndicator_ the same API as toolbar_. if (this.pageIndicator_) { + const lastIndex = this.pageIndicator_.index; this.pageIndicator_.index = visiblePage; if (this.documentDimensions_.pageDimensions.length > 1 && - hasScrollbars.vertical) { + hasScrollbars.vertical && lastIndex !== undefined) { this.pageIndicator_.style.visibility = 'visible'; } else { this.pageIndicator_.style.visibility = 'hidden'; @@ -921,16 +1031,15 @@ PDFViewer.prototype = { viewportWidth: size.width, viewportHeight: size.height }); - }, + } /** * Handle a scripting message from outside the extension (typically sent by * PDFScriptingAPI in a page containing the extension) to interact with the * plugin. - * - * @param {MessageObject} message the message to handle. + * @param {!MessageObject} message The message to handle. */ - handleScriptingMessage: function(message) { + handleScriptingMessage(message) { if (this.parentWindow_ != message.source) { this.parentWindow_ = message.source; this.parentOrigin_ = message.origin; @@ -953,30 +1062,38 @@ PDFViewer.prototype = { switch (message.data.type.toString()) { case 'getSelectedText': + this.pluginController_.getSelectedText(); + break; case 'print': + this.pluginController_.print(); + break; case 'selectAll': - this.pluginController_.postMessage(message.data); + this.pluginController_.selectAll(); break; } - }, + } /** * Handle scripting messages specific to print preview. - * - * @param {MessageObject} message the message to handle. + * @param {!MessageObject} message the message to handle. * @return {boolean} true if the message was handled, false otherwise. * @private */ - handlePrintPreviewScriptingMessage_: function(message) { + handlePrintPreviewScriptingMessage_(message) { if (!this.isPrintPreview_) { return false; } - switch (message.data.type.toString()) { + let messageData = message.data; + switch (messageData.type.toString()) { case 'loadPreviewPage': - this.pluginController_.postMessage(message.data); + messageData = + /** @type {{ url: string, index: number }} */ (messageData); + this.pluginController_.loadPreviewPage( + messageData.url, messageData.index); return true; case 'resetPrintPreviewMode': + messageData = /** @type {!PrintPreviewParams} */ (messageData); this.setLoadState_(LoadState.LOADING); if (!this.inPrintPreviewMode_) { this.inPrintPreviewMode_ = true; @@ -999,49 +1116,42 @@ PDFViewer.prototype = { saveButton.parentNode.removeChild(saveButton); } - this.pageIndicator_.pageLabels = message.data.pageNumbers; + this.pageIndicator_.pageLabels = messageData.pageNumbers; - this.pluginController_.postMessage({ - type: 'resetPrintPreviewMode', - url: message.data.url, - grayscale: message.data.grayscale, - // If the PDF isn't modifiable we send 0 as the page count so that no - // blank placeholder pages get appended to the PDF. - pageCount: - (message.data.modifiable ? message.data.pageNumbers.length : 0) - }); + this.pluginController_.resetPrintPreviewMode(messageData); return true; case 'sendKeyEvent': - this.handleKeyEvent_(DeserializeKeyEvent(message.data.keyEvent)); + this.handleKeyEvent_(/** @type {!KeyboardEvent} */ (DeserializeKeyEvent( + /** @type {{ keyEvent: Object }} */ (message.data).keyEvent))); return true; case 'hideToolbars': this.toolbarManager_.resetKeyboardNavigationAndHideToolbars(); return true; case 'darkModeChanged': - this.dark_ = message.data.darkMode; + this.dark_ = /** @type {{darkMode: boolean}} */ (message.data).darkMode; if (this.isPrintPreview_) { this.sendBackgroundColorForPrintPreview_(); } return true; case 'scrollPosition': const position = this.viewport_.position; - position.y += message.data.y; - position.x += message.data.x; - this.viewport.position = position; + messageData = /** @type {{ x: number, y: number }} */ (message.data); + position.y += messageData.y; + position.x += messageData.x; + this.viewport_.position = position; return true; } return false; - }, + } /** * Send a scripting message outside the extension (typically to * PDFScriptingAPI in a page containing the extension). - * * @param {Object} message the message to send. * @private */ - sendScriptingMessage_: function(message) { + sendScriptingMessage_(message) { if (this.parentWindow_ && this.parentOrigin_) { let targetOrigin; // Only send data back to the embedder if it is from the same origin, @@ -1057,34 +1167,73 @@ PDFViewer.prototype = { } this.parentWindow_.postMessage(message, targetOrigin); } - }, - - /** - * @type {Viewport} the viewport of the PDF viewer. - */ - get viewport() { - return this.viewport_; - }, + } /** - * Each bookmark is an Object containing a: - * - title - * - page (optional) - * - array of children (themselves bookmarks) - * - * @type {Array} the top-level bookmarks of the PDF. + * @param {!CustomEvent<MessageData>} e + * @private */ - get bookmarks() { - return this.bookmarks_; - }, + handlePluginMessage_(e) { + const data = e.detail; + switch (data.type.toString()) { + case 'beep': + this.handleBeep_(); + return; + case 'documentDimensions': + this.setDocumentDimensions_( + /** @type {!DocumentDimensionsMessageData} */ (data)); + return; + case 'getPassword': + this.handlePasswordRequest_(); + return; + case 'getSelectedTextReply': + this.handleSelectedTextReply_( + /** @type {{ selectedText: string }} */ (data).selectedText); + return; + case 'loadProgress': + this.updateProgress_( + /** @type {{ progress: number }} */ (data).progress); + return; + case 'navigate': + const navigateData = /** @type {!NavigateMessageData} */ (data); + this.handleNavigate_(navigateData.url, navigateData.disposition); + return; + case 'navigateToDestination': + const destinationData = /** @type {!DestinationMessageData} */ (data); + this.handleNavigateToDestination_( + destinationData.page, destinationData.x, destinationData.y, + destinationData.zoom); + return; + case 'printPreviewLoaded': + this.handlePrintPreviewLoaded_(); + return; + case 'metadata': + const metadata = /** @type {!MetadataMessageData} */ (data); + this.setDocumentMetadata_( + metadata.title, metadata.bookmarks, metadata.canSerializeDocument); + return; + case 'setIsSelecting': + this.setIsSelecting_( + /** @type {{ isSelecting: boolean }} */ (data).isSelecting); + return; + case 'getNamedDestinationReply': + this.paramsParser_.onNamedDestinationReceived( + /** @type {{ pageNumber: number }} */ (data).pageNumber); + return; + case 'formFocusChange': + this.isFormFieldFocused_ = + /** @type {{ focused: boolean }} */ (data).focused; + return; + } + assertNotReached('Unknown message type received: ' + data.type); + } /** * Sets document dimensions from the current controller. - * - * @param {{height: number, width: number, pageDimensions: Array}} - * documentDimensions + * @param {!DocumentDimensionsMessageData} documentDimensions + * @private */ - setDocumentDimensions: function(documentDimensions) { + setDocumentDimensions_(documentDimensions) { this.documentDimensions_ = documentDimensions; this.isUserInitiatedEvent_ = false; this.viewport_.setDocumentDimensions(this.documentDimensions_); @@ -1098,20 +1247,22 @@ PDFViewer.prototype = { if (this.toolbar_) { this.toolbar_.docLength = this.documentDimensions_.pageDimensions.length; } - }, + } /** * Handles a beep request from the current controller. + * @private */ - handleBeep: function() { + handleBeep_() { // Beeps are annoying, so just track count for now. this.beepCount_ += 1; - }, + } /** * Handles a password request from the current controller. + * @private */ - handlePasswordRequest: function() { + handlePasswordRequest_() { // If the password screen isn't up, put it up. Otherwise we're // responding to an incorrect password so deny it. if (!this.passwordScreen_.active) { @@ -1121,26 +1272,27 @@ PDFViewer.prototype = { } else { this.passwordScreen_.deny(); } - }, + } /** * Handles a selected text reply from the current controller. * @param {string} selectedText + * @private */ - handleSelectedTextReply: function(selectedText) { + handleSelectedTextReply_(selectedText) { this.sendScriptingMessage_({ type: 'getSelectedTextReply', selectedText: selectedText, }); - }, + } /** * Handles a navigation request from the current controller. - * * @param {string} url - * @param {string} disposition + * @param {!PdfNavigator.WindowOpenDisposition} disposition + * @private */ - handleNavigate: function(url, disposition) { + handleNavigate_(url, disposition) { // If in print preview, always open a new tab. if (this.isPrintPreview_) { this.navigator_.navigate( @@ -1148,24 +1300,48 @@ PDFViewer.prototype = { } else { this.navigator_.navigate(url, disposition); } - }, + } + + /** + * Handles an internal navigation request to a destination from the current + * controller. + * + * @param {number} page + * @param {number} x + * @param {number} y + * @param {number} zoom + * @private + */ + handleNavigateToDestination_(page, x, y, zoom) { + if (zoom) { + this.viewport_.setZoom(zoom); + } + + if (x || y) { + this.viewport_.goToPageAndXY(page, x ? x : 0, y ? y : 0); + } else { + this.viewport_.goToPage(page); + } + } /** * Handles a notification that print preview has loaded from the * current controller. + * @private */ - handlePrintPreviewLoaded: function() { + handlePrintPreviewLoaded_() { this.isPrintPreviewLoadingFinished_ = true; this.sendDocumentLoadedMessage_(); - }, + } /** * Sets document metadata from the current controller. * @param {string} title - * @param {Array} bookmarks + * @param {!Array<!Bookmark>} bookmarks * @param {boolean} canSerializeDocument + * @private */ - setDocumentMetadata: function(title, bookmarks, canSerializeDocument) { + setDocumentMetadata_(title, bookmarks, canSerializeDocument) { if (title) { document.title = title; } else { @@ -1174,47 +1350,40 @@ PDFViewer.prototype = { this.bookmarks_ = bookmarks; if (this.toolbar_) { this.toolbar_.docTitle = document.title; - this.toolbar_.bookmarks = this.bookmarks; + this.toolbar_.bookmarks = this.bookmarks_; } this.canSerializeDocument_ = canSerializeDocument; this.updateAnnotationAvailable_(); - }, + } /** * Sets the is selecting flag from the current controller. * @param {boolean} isSelecting + * @private */ - setIsSelecting: function(isSelecting) { + setIsSelecting_(isSelecting) { this.viewportScroller_.setEnableScrolling(isSelecting); - }, - - /** - * Sets the form field focused flag from the current controller. - * @param {boolean} focused - */ - setIsFormFieldFocused: function(focused) { - this.isFormFieldFocused_ = focused; - }, + } /** * An event handler for when the browser tells the PDF Viewer to perform a * save. - * * @param {string} streamUrl unique identifier for a PDF Viewer instance. * @private */ - onSave: async function(streamUrl) { + async onSave_(streamUrl) { if (streamUrl != this.browserApi_.getStreamInfo().streamUrl) { return; } - this.save(); - }, + this.save_(); + } /** * Saves the current PDF document to disk. + * @private */ - save: async function() { + async save_() { PDFMetrics.record(PDFMetrics.UserAction.SAVE); if (this.hasEnteredAnnotationMode_) { PDFMetrics.record(PDFMetrics.UserAction.SAVE_WITH_ANNOTATION); @@ -1257,17 +1426,19 @@ PDFViewer.prototype = { // Saving in Annotation mode is destructive: crbug.com/919364 this.exitAnnotationMode_(); - }, + } - print: async function() { + /** @private */ + async print_() { PDFMetrics.record(PDFMetrics.UserAction.PRINT); await this.exitAnnotationMode_(); this.currentController_.print(); - }, + } /** * Updates the toolbar's annotation available flag depending on current * conditions. + * @private */ updateAnnotationAvailable_() { if (!this.toolbar_) { @@ -1284,408 +1455,67 @@ PDFViewer.prototype = { annotationAvailable = false; } this.toolbar_.annotationAvailable = annotationAvailable; - }, + } - rotateClockwise() { + /** @private */ + rotateClockwise_() { PDFMetrics.record(PDFMetrics.UserAction.ROTATE); this.viewport_.rotateClockwise(1); this.currentController_.rotateClockwise(); this.updateAnnotationAvailable_(); - }, + } - rotateCounterclockwise() { + /** @private */ + rotateCounterclockwise_() { PDFMetrics.record(PDFMetrics.UserAction.ROTATE); this.viewport_.rotateClockwise(3); this.currentController_.rotateCounterclockwise(); this.updateAnnotationAvailable_(); - }, - - setHasUnsavedChanges: function() { - // Warn the user if they attempt to close the window without saving. - chrome.mimeHandlerPrivate.setShowBeforeUnloadDialog(true); - }, - - /** @param {UndoState} state */ - setAnnotationUndoState(state) { - this.toolbar_.canUndoAnnotation = state.canUndo; - this.toolbar_.canRedoAnnotation = state.canRedo; - }, -}; - -/** @abstract */ -class ContentController { - constructor() {} - - /** - * A callback that's called before the zoom changes. - */ - beforeZoom() {} - - /** - * A callback that's called after the zoom changes. - */ - afterZoom() {} - - /** - * Handles a change to the viewport. - */ - viewportChanged() {} - - /** - * Rotates the document 90 degrees in the clockwise direction. - * @abstract - */ - rotateClockwise() {} - - /** - * Rotates the document 90 degrees in the counter clockwise direction. - * @abstract - */ - rotateCounterclockwise() {} - - /** - * Triggers printing of the current document. - */ - print() {} - - /** - * Undo an edit action. - */ - undo() {} - - /** - * Redo an edit action. - */ - redo() {} - - /** - * Requests that the current document be saved. - * @param {boolean} requireResult whether a response is required, otherwise - * the controller may save the document to disk internally. - * @return {Promise<{fileName: string, dataToSave: ArrayBuffer}} - * @abstract - */ - save(requireResult) {} - - /** - * Loads PDF document from `data` activates UI. - * @param {string} fileName - * @param {ArrayBuffer} data - * @return {Promise<void>} - * @abstract - */ - load(fileName, data) {} - - /** - * Unloads the current document and removes the UI. - * @abstract - */ - unload() {} -} - -class InkController extends ContentController { - /** - * @param {PDFViewer} viewer - * @param {Viewport} viewport - */ - constructor(viewer, viewport) { - super(); - this.viewer_ = viewer; - this.viewport_ = viewport; - - /** @type {ViewerInkHost} */ - this.inkHost_ = null; } - /** @param {AnnotationTool} tool */ - setAnnotationTool(tool) { - this.tool_ = tool; - if (this.inkHost_) { - this.inkHost_.setAnnotationTool(tool); - } - } - - /** @override */ - rotateClockwise() { - // TODO(dstockwell): implement rotation - } - - /** @override */ - rotateCounterclockwise() { - // TODO(dstockwell): implement rotation - } - - /** @override */ - viewportChanged() { - this.inkHost_.viewportChanged(); - } - - /** @override */ - save(requireResult) { - return this.inkHost_.saveDocument(); - } - - /** @override */ - undo() { - this.inkHost_.undo(); - } - - /** @override */ - redo() { - this.inkHost_.redo(); - } - - /** @override */ - load(filename, data) { - if (!this.inkHost_) { - this.inkHost_ = document.createElement('viewer-ink-host'); - $('content').appendChild(this.inkHost_); - this.inkHost_.viewport = this.viewport_; - this.inkHost_.addEventListener('stroke-added', e => { - this.viewer_.setHasUnsavedChanges(); - }); - this.inkHost_.addEventListener('undo-state-changed', e => { - this.viewer_.setAnnotationUndoState(e.detail); - }); - } - return this.inkHost_.load(filename, data); - } - - /** @override */ - unload() { - this.inkHost_.remove(); - this.inkHost_ = null; - } -} - -class PluginController extends ContentController { /** - * @param {HTMLEmbedElement} plugin - * @param {PDFViewer} viewer - * @param {Viewport} viewport - */ - constructor(plugin, viewer, viewport) { - super(); - this.plugin_ = plugin; - this.viewer_ = viewer; - this.viewport_ = viewport; - - /** @private {!Map<string, PromiseResolver>} */ - this.pendingTokens_ = new Map(); - this.plugin_.addEventListener( - 'message', e => this.handlePluginMessage_(e), false); - } - - /** - * Notify the plugin to stop reacting to scroll events while zoom is taking - * place to avoid flickering. - * @override - */ - beforeZoom() { - this.postMessage({type: 'stopScrolling'}); - - if (this.viewport_.pinchPhase == Viewport.PinchPhase.PINCH_START) { - const position = this.viewport_.position; - const zoom = this.viewport_.getZoom(); - const pinchPhase = this.viewport_.pinchPhase; - this.postMessage({ - type: 'viewport', - userInitiated: true, - zoom: zoom, - xOffset: position.x, - yOffset: position.y, - pinchPhase: pinchPhase - }); - } - } - - /** - * Notify the plugin of the zoom change and to continue reacting to scroll - * events. - * @override - */ - afterZoom() { - const position = this.viewport_.position; - 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; - - this.postMessage({ - type: 'viewport', - userInitiated: this.viewer_.isUserInitiatedEvent_, - zoom: zoom, - xOffset: position.x, - yOffset: position.y, - pinchPhase: pinchPhase, - pinchX: pinchCenter.x, - pinchY: pinchCenter.y, - pinchVectorX: pinchVector.x, - pinchVectorY: pinchVector.y - }); - } - - // TODO(dstockwell): this method should be private, add controller APIs that - // map to all of the existing usage. crbug.com/913279 - /** - * Post a message to the PPAPI plugin. Some messages will cause an async reply - * to be received through handlePluginMessage_(). - * - * @param {Object} message Message to post. + * @param {!CustomEvent<{canUndo: boolean, canRedo: boolean}>} e + * @private */ - postMessage(message) { - this.plugin_.postMessage(message); - } - - /** @override */ - rotateClockwise() { - this.postMessage({type: 'rotateClockwise'}); - } - - /** @override */ - rotateCounterclockwise() { - this.postMessage({type: 'rotateCounterclockwise'}); - } - - /** @override */ - print() { - this.postMessage({type: 'print'}); - } - - /** @override */ - save(requireResult) { - const resolver = new PromiseResolver(); - const newToken = createToken(); - this.pendingTokens_.set(newToken, resolver); - this.postMessage({type: 'save', token: newToken, force: requireResult}); - return resolver.promise; + setAnnotationUndoState_(e) { + this.toolbar_.canUndoAnnotation = e.detail.canUndo; + this.toolbar_.canRedoAnnotation = e.detail.canRedo; } - /** @override */ - async load(fileName, data) { - const url = URL.createObjectURL(new Blob([data])); - this.plugin_.removeAttribute('headers'); - this.plugin_.setAttribute('stream-url', url); - this.plugin_.style.display = 'block'; - try { - await this.viewer_.loaded; - } finally { - URL.revokeObjectURL(url); - } - } - - /** @override */ - unload() { - this.plugin_.style.display = 'none'; - } - - /** - * An event handler for handling message events received from the plugin. - * - * @param {MessageObject} message a message event. - * @private - */ - handlePluginMessage_(message) { - switch (message.data.type.toString()) { - case 'beep': - this.viewer_.handleBeep(); - break; - case 'documentDimensions': - this.viewer_.setDocumentDimensions(message.data); - break; - case 'email': - const href = 'mailto:' + message.data.to + '?cc=' + message.data.cc + - '&bcc=' + message.data.bcc + '&subject=' + message.data.subject + - '&body=' + message.data.body; - window.location.href = href; - break; - case 'getPassword': - this.viewer_.handlePasswordRequest(); - break; - case 'getSelectedTextReply': - this.viewer_.handleSelectedTextReply(message.data.selectedText); - break; - case 'goToPage': - this.viewport_.goToPage(message.data.page); - break; - case 'loadProgress': - this.viewer_.updateProgress(message.data.progress); - break; - case 'navigate': - this.viewer_.handleNavigate(message.data.url, message.data.disposition); - break; - case 'printPreviewLoaded': - this.viewer_.handlePrintPreviewLoaded(); - break; - case 'setScrollPosition': - this.viewport_.scrollTo(/** @type {!PartialPoint} */ (message.data)); - break; - case 'scrollBy': - this.viewport_.scrollBy(/** @type {!Point} */ (message.data)); - break; - case 'metadata': - this.viewer_.setDocumentMetadata( - message.data.title, message.data.bookmarks, - message.data.canSerializeDocument); - break; - case 'setIsSelecting': - this.viewer_.setIsSelecting(message.data.isSelecting); - break; - case 'getNamedDestinationReply': - this.viewer_.paramsParser_.onNamedDestinationReceived( - message.data.pageNumber); - break; - case 'formFocusChange': - this.viewer_.setIsFormFieldFocused(message.data.focused); - break; - case 'saveData': - this.saveData_(message.data); - break; - case 'consumeSaveToken': - const resolver = this.pendingTokens_.get(message.data.token); - assert(this.pendingTokens_.delete(message.data.token)); - resolver.resolve(null); - break; + /** @private */ + resetTrackers_() { + this.viewport_.resetTracker(); + if (this.tracker_) { + this.tracker_.removeAll(); } } +} - /** - * Handles the pdf file buffer received from the plugin. - * - * @param {SaveDataMessageData} messageData data of the message event. - * @private - */ - saveData_(messageData) { - assert( - loadTimeData.getBoolean('pdfFormSaveEnabled') || - loadTimeData.getBoolean('pdfAnnotationsEnabled')); - - // Verify a token that was created by this instance is included to avoid - // being spammed. - const resolver = this.pendingTokens_.get(messageData.token); - assert(this.pendingTokens_.delete(messageData.token)); +/** + * The height of the toolbar along the top of the page. The document will be + * shifted down by this much in the viewport. + */ +PDFViewer.MATERIAL_TOOLBAR_HEIGHT = 56; - if (!messageData.dataToSave) { - resolver.reject(); - return; - } +/** + * Minimum height for the material toolbar to show (px). Should match the media + * query in index-material.css. If the window is smaller than this at load, + * leave no space for the toolbar. + */ +PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT = 250; - // Verify the file size and the first bytes to make sure it's a PDF. Cap at - // 100 MB. This cap should be kept in sync with and is also enforced in - // pdf/out_of_process_instance.cc. - const MIN_FILE_SIZE = '%PDF1.0'.length; - const MAX_FILE_SIZE = 100 * 1000 * 1000; +/** + * The background color used for print preview (--google-grey-refresh-300). + */ +PDFViewer.PRINT_PREVIEW_BACKGROUND_COLOR = '0xFFDADCE0'; - const buffer = messageData.dataToSave; - const bufView = new Uint8Array(buffer); - assert( - bufView.length <= MAX_FILE_SIZE, - `File too large to be saved: ${bufView.length} bytes.`); - assert(bufView.length >= MIN_FILE_SIZE); - assert( - String.fromCharCode(bufView[0], bufView[1], bufView[2], bufView[3]) == - '%PDF'); +/** + * The background color used for print preview when dark mode is enabled + * (--google-grey-refresh-700). + */ +PDFViewer.PRINT_PREVIEW_DARK_BACKGROUND_COLOR = '0xFF5F6368'; - resolver.resolve(messageData); - } -} +/** + * The background color used for the regular viewer. + */ +PDFViewer.BACKGROUND_COLOR = '0xFF525659'; diff --git a/chromium/chrome/browser/resources/pdf/toolbar_manager.js b/chromium/chrome/browser/resources/pdf/toolbar_manager.js index 255bc3394f1..8a2b06c135c 100644 --- a/chromium/chrome/browser/resources/pdf/toolbar_manager.js +++ b/chromium/chrome/browser/resources/pdf/toolbar_manager.js @@ -20,10 +20,8 @@ const TOP_TOOLBAR_REVEAL_DISTANCE = 100; const SIDE_TOOLBAR_REVEAL_DISTANCE_RIGHT = 150; const SIDE_TOOLBAR_REVEAL_DISTANCE_BOTTOM = 250; - - /** - * @param {MouseEvent} e Event to test. + * @param {!MouseEvent} e Event to test. * @return {boolean} True if the mouse is close to the top of the screen. */ function isMouseNearTopToolbar(e) { @@ -31,7 +29,7 @@ function isMouseNearTopToolbar(e) { } /** - * @param {MouseEvent} e Event to test. + * @param {!MouseEvent} e Event to test. * @param {Window} window Window to test against. * @param {boolean} reverse Whether the side toolbar is reversed. * @return {boolean} True if the mouse is close to the bottom-right of the @@ -47,47 +45,60 @@ function isMouseNearSideToolbar(e, window, reverse) { return atSide && atBottom; } -/** - * Constructs a Toolbar Manager, responsible for co-ordinating between multiple - * toolbar elements. - * - * @param {Object} window The window containing the UI. - * @param {Object} toolbar The top toolbar element. - * @param {Object} zoomToolbar The zoom toolbar element. - * @constructor - */ -function ToolbarManager(window, toolbar, zoomToolbar) { - this.window_ = window; - this.toolbar_ = toolbar; - this.zoomToolbar_ = zoomToolbar; +/** Responsible for co-ordinating between multiple toolbar elements. */ +class ToolbarManager { + /** + * @param {!Window} window The window containing the UI. + * @param {?ViewerPdfToolbarElement} toolbar + * @param {!ViewerZoomToolbarElement} zoomToolbar + */ + constructor(window, toolbar, zoomToolbar) { + /** @private {!Window} */ + this.window_ = window; - this.toolbarTimeout_ = null; - this.isMouseNearTopToolbar_ = false; - this.isMouseNearSideToolbar_ = false; + /** @private {?ViewerPdfToolbarElement} */ + this.toolbar_ = toolbar; - this.sideToolbarAllowedOnly_ = false; - this.sideToolbarAllowedOnlyTimer_ = null; + /** @private {!ViewerZoomToolbarElement} */ + this.zoomToolbar_ = zoomToolbar; - this.keyboardNavigationActive = false; + /** @private {?number} */ + this.toolbarTimeout_ = null; - this.lastMovementTimestamp = null; + /** @private {boolean} */ + this.isMouseNearTopToolbar_ = false; - this.reverseSideToolbar_ = false; + /** @private {boolean} */ + this.isMouseNearSideToolbar_ = false; - this.window_.addEventListener('resize', this.resizeDropdowns_.bind(this)); - this.resizeDropdowns_(); + /** @private {boolean} */ + this.sideToolbarAllowedOnly_ = false; - if (zoomToolbar.isPrintPreview()) { - this.zoomToolbar_.addEventListener('keyboard-navigation-active', e => { - this.keyboardNavigationActive = e.detail; - }); - } -} + /** @private {?number} */ + this.sideToolbarAllowedOnlyTimer_ = null; + + /** @private {boolean} */ + this.keyboardNavigationActive = false; + + /** @private {?number} */ + this.lastMovementTimestamp = null; -ToolbarManager.prototype = { + /** @private {boolean} */ + this.reverseSideToolbar_ = false; + + this.window_.addEventListener('resize', this.resizeDropdowns_.bind(this)); + this.resizeDropdowns_(); + + if (zoomToolbar.isPrintPreview()) { + this.zoomToolbar_.addEventListener('keyboard-navigation-active', e => { + this.keyboardNavigationActive = e.detail; + }); + } + } - handleMouseMove: function(e) { - this.isMouseNearTopToolbar_ = this.toolbar_ && isMouseNearTopToolbar(e); + /** @param {!MouseEvent} e */ + handleMouseMove(e) { + this.isMouseNearTopToolbar_ = !!this.toolbar_ && isMouseNearTopToolbar(e); this.isMouseNearSideToolbar_ = isMouseNearSideToolbar(e, this.window_, this.reverseSideToolbar_); @@ -123,17 +134,16 @@ ToolbarManager.prototype = { } } this.hideToolbarsAfterTimeout(); - }, + } /** * Whether a mousemove event is high enough velocity to reveal the toolbars. - * - * @param {MouseEvent} e Event to test. + * @param {!MouseEvent} e Event to test. * @return {boolean} true if the event is a high velocity mousemove, false * otherwise. * @private */ - isHighVelocityMouseMove_: function(e) { + isHighVelocityMouseMove_(e) { if (e.type == 'mousemove') { if (this.lastMovementTimestamp == null) { this.lastMovementTimestamp = this.getCurrentTimestamp_(); @@ -150,54 +160,51 @@ ToolbarManager.prototype = { } } return false; - }, + } /** * Wrapper around Date.now() to make it easily replaceable for testing. - * * @return {number} * @private */ - getCurrentTimestamp_: function() { + getCurrentTimestamp_() { return Date.now(); - }, + } - /** - * Display both UI toolbars. - */ - showToolbars: function() { + /** Display both UI toolbars. */ + showToolbars() { if (this.toolbar_) { this.toolbar_.show(); } this.zoomToolbar_.show(); - }, + } /** * Show toolbars and mark that navigation is being performed with * tab/shift-tab. This disables toolbar hiding until the mouse is moved or * escape is pressed. */ - showToolbarsForKeyboardNavigation: function() { + showToolbarsForKeyboardNavigation() { this.keyboardNavigationActive = true; this.showToolbars(); - }, + } /** * Hide toolbars after a delay, regardless of the position of the mouse. * Intended to be called when the mouse has moved out of the parent window. */ - hideToolbarsForMouseOut: function() { + hideToolbarsForMouseOut() { this.isMouseNearTopToolbar_ = false; this.isMouseNearSideToolbar_ = false; this.hideToolbarsAfterTimeout(); - }, + } /** * Check if the toolbars are able to be closed, and close them if they are. * Toolbars may be kept open based on mouse/keyboard activity and active * elements. */ - hideToolbarsIfAllowed: function() { + hideToolbarsIfAllowed() { if (this.isMouseNearSideToolbar_ || this.isMouseNearTopToolbar_) { return; } @@ -221,47 +228,44 @@ ToolbarManager.prototype = { this.toolbar_.hide(); } this.zoomToolbar_.hide(); - }, + } - /** - * Hide the toolbar after the HIDE_TIMEOUT has elapsed. - */ - hideToolbarsAfterTimeout: function() { + /** Hide the toolbars after the HIDE_TIMEOUT has elapsed. */ + hideToolbarsAfterTimeout() { if (this.toolbarTimeout_) { this.window_.clearTimeout(this.toolbarTimeout_); } this.toolbarTimeout_ = this.window_.setTimeout( this.hideToolbarsIfAllowed.bind(this), HIDE_TIMEOUT); - }, + } /** * Hide the 'topmost' layer of toolbars. Hides any dropdowns that are open, or * hides the basic toolbars otherwise. */ - hideSingleToolbarLayer: function() { + hideSingleToolbarLayer() { if (!this.toolbar_ || !this.toolbar_.hideDropdowns()) { this.keyboardNavigationActive = false; this.hideToolbarsIfAllowed(); } - }, + } /** * Clears the keyboard navigation state and hides the toolbars after a delay. */ - resetKeyboardNavigationAndHideToolbars: function() { + resetKeyboardNavigationAndHideToolbars() { this.keyboardNavigationActive = false; this.hideToolbarsAfterTimeout(); - }, + } /** * Hide the top toolbar and keep it hidden until both: * - The mouse is moved away from the right side of the screen * - 1 second has passed. - * * The top toolbar can be immediately re-opened by moving the mouse to the top * of the screen. */ - forceHideTopToolbar: function() { + forceHideTopToolbar() { if (!this.toolbar_) { return; } @@ -270,20 +274,19 @@ ToolbarManager.prototype = { this.sideToolbarAllowedOnlyTimer_ = this.window_.setTimeout(() => { this.sideToolbarAllowedOnlyTimer_ = null; }, FORCE_HIDE_TIMEOUT); - }, + } /** Reverse the position of the side toolbar. */ - reverseSideToolbar: function() { + reverseSideToolbar() { this.reverseSideToolbar_ = true; - }, + } /** * Updates the size of toolbar dropdowns based on the positions of the rest of * the UI. - * * @private */ - resizeDropdowns_: function() { + resizeDropdowns_() { if (!this.toolbar_) { return; } @@ -291,4 +294,4 @@ ToolbarManager.prototype = { this.window_.innerHeight - this.zoomToolbar_.clientHeight; this.toolbar_.setDropdownLowerBound(lowerBound); } -}; +} diff --git a/chromium/chrome/browser/resources/pdf/viewport.js b/chromium/chrome/browser/resources/pdf/viewport.js index 480664c014c..17986ae5d81 100644 --- a/chromium/chrome/browser/resources/pdf/viewport.js +++ b/chromium/chrome/browser/resources/pdf/viewport.js @@ -6,14 +6,21 @@ * @typedef {{ * width: number, * height: number, + * layoutOptions: (!LayoutOptions|undefined), * pageDimensions: Array<ViewportRect>, * }} */ let DocumentDimensions; +/** @typedef {{defaultPageOrientation: number}} */ +let LayoutOptions; + /** @typedef {{x: number, y: number}} */ let Point; +/** @typedef {{x: (number|undefined), y: (number|undefined)}} */ +let PartialPoint; + /** @typedef {{width: number, height: number}} */ let Size; @@ -279,6 +286,15 @@ class Viewport { } /** + * @return {!LayoutOptions|undefined} A dictionary carrying layout options + * from the plugin. + */ + getLayoutOptions() { + return this.documentDimensions_ ? this.documentDimensions_.layoutOptions : + undefined; + } + + /** * @return {!ViewportRect} ViewportRect for the viewport given current zoom. * @private */ @@ -605,7 +621,7 @@ class Viewport { const bottom = this.pageDimensions_[page].y + this.pageDimensions_[page].height; - if (top <= y && bottom > y) { + if (top <= y && y <= bottom) { return page; } @@ -1166,7 +1182,9 @@ class Viewport { this.fittingType_ == FittingType.FIT_TO_HEIGHT); } - /** @param {!Point} point The position to which to scroll the viewport. */ + /** + * @param {!PartialPoint} point The position to which to scroll the viewport. + */ scrollTo(point) { let changed = false; const newPosition = this.position; diff --git a/chromium/chrome/browser/resources/pdf/viewport_scroller.js b/chromium/chrome/browser/resources/pdf/viewport_scroller.js index bb8c187d478..ca16c9cf7d2 100644 --- a/chromium/chrome/browser/resources/pdf/viewport_scroller.js +++ b/chromium/chrome/browser/resources/pdf/viewport_scroller.js @@ -5,75 +5,61 @@ 'use strict'; /** - * The period of time in milliseconds to wait between updating the viewport - * position by the scroll velocity. - * - * @private - */ -ViewportScroller.DRAG_TIMER_INTERVAL_MS_ = 100; - -/** - * The maximum drag scroll distance per DRAG_TIMER_INTERVAL in pixels. - * - * @private - */ -ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_ = 100; - -/** * Creates a new ViewportScroller. * A ViewportScroller scrolls the page in response to drag selection with the * mouse. * - * @param {Object} viewport The viewport info of the page. - * @param {Object} plugin The PDF plugin element. - * @param {Object} window The window containing the viewer. - * @constructor */ -function ViewportScroller(viewport, plugin, window) { - this.viewport_ = viewport; - this.plugin_ = plugin; - this.window_ = window; - this.mousemoveCallback_ = null; - this.timerId_ = null; - this.scrollVelocity_ = null; - this.lastFrameTime_ = 0; -} +class ViewportScroller { + /** + * @param {Object} viewport The viewport info of the page. + * @param {Object} plugin The PDF plugin element. + * @param {Object} window The window containing the viewer. + */ + constructor(viewport, plugin, window) { + this.viewport_ = viewport; + this.plugin_ = plugin; + this.window_ = window; + this.mousemoveCallback_ = null; + this.timerId_ = null; + this.scrollVelocity_ = null; + this.lastFrameTime_ = 0; + } -ViewportScroller.prototype = { /** * Start scrolling the page by |scrollVelocity_| every * |DRAG_TIMER_INTERVAL_MS_|. * * @private */ - startDragScrollTimer_: function() { + startDragScrollTimer_() { if (this.timerId_ === null) { this.timerId_ = this.window_.setInterval( this.dragScrollPage_.bind(this), ViewportScroller.DRAG_TIMER_INTERVAL_MS_); this.lastFrameTime_ = Date.now(); } - }, + } /** * Stops the drag scroll timer if it is active. * * @private */ - stopDragScrollTimer_: function() { + stopDragScrollTimer_() { if (this.timerId_ !== null) { this.window_.clearInterval(this.timerId_); this.timerId_ = null; this.lastFrameTime_ = 0; } - }, + } /** * Scrolls the viewport by the current scroll velocity. * * @private */ - dragScrollPage_: function() { + dragScrollPage_() { const position = this.viewport_.position; const currentFrameTime = Date.now(); const timeAdjustment = (currentFrameTime - this.lastFrameTime_) / @@ -82,7 +68,7 @@ ViewportScroller.prototype = { position.x += (this.scrollVelocity_.x * timeAdjustment); this.viewport_.position = position; this.lastFrameTime_ = currentFrameTime; - }, + } /** * Calculate the velocity to scroll while dragging using the distance of the @@ -92,7 +78,7 @@ ViewportScroller.prototype = { * @return {Object} Object with x and y direction scroll velocity. * @private */ - calculateVelocity_: function(event) { + calculateVelocity_(event) { const x = Math.min( Math.max( @@ -106,7 +92,7 @@ ViewportScroller.prototype = { ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_) * Math.sign(event.offsetY); return {x: x, y: y}; - }, + } /** * Handles mousemove events. It updates the scroll velocity and starts and @@ -115,14 +101,14 @@ ViewportScroller.prototype = { * @param {Object} event The mousemove event. * @private */ - onMousemove_: function(event) { + onMousemove_(event) { this.scrollVelocity_ = this.calculateVelocity_(event); if (!this.scrollVelocity_.x && !this.scrollVelocity_.y) { this.stopDragScrollTimer_(); } else if (!this.timerId_) { this.startDragScrollTimer_(); } - }, + } /** * Sets whether to scroll the viewport when the mouse is outside the @@ -130,7 +116,7 @@ ViewportScroller.prototype = { * * @param {boolean} isSelecting Represents selection status. */ - setEnableScrolling: function(isSelecting) { + setEnableScrolling(isSelecting) { if (isSelecting) { if (!this.mousemoveCallback_) { this.mousemoveCallback_ = this.onMousemove_.bind(this); @@ -145,4 +131,19 @@ ViewportScroller.prototype = { } } } -}; +} + +/** + * The period of time in milliseconds to wait between updating the viewport + * position by the scroll velocity. + * + * @private + */ +ViewportScroller.DRAG_TIMER_INTERVAL_MS_ = 100; + +/** + * The maximum drag scroll distance per DRAG_TIMER_INTERVAL in pixels. + * + * @private + */ +ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_ = 100; |