From c551f43206405019121bd2b2c93714319a0a3300 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 23 Jan 2020 17:21:03 +0100 Subject: BASELINE: Update Chromium to 79.0.3945.139 Change-Id: I336b7182fab9bca80b709682489c07db112eaca5 Reviewed-by: Allan Sandfeld Jensen --- chromium/chrome/browser/resources/pdf/BUILD.gn | 57 +- .../browser/resources/pdf/annotation_tool.js | 15 + .../chrome/browser/resources/pdf/controller.js | 494 +++++++ .../chrome/browser/resources/pdf/elements/BUILD.gn | 118 ++ .../resources/pdf/elements/viewer-bookmark.html | 72 + .../resources/pdf/elements/viewer-bookmark.js | 140 ++ .../pdf/elements/viewer-bookmark/BUILD.gn | 17 - .../elements/viewer-bookmark/viewer-bookmark.html | 72 - .../elements/viewer-bookmark/viewer-bookmark.js | 133 -- .../pdf/elements/viewer-error-screen.html | 21 + .../resources/pdf/elements/viewer-error-screen.js | 22 + .../pdf/elements/viewer-error-screen/BUILD.gn | 17 - .../viewer-error-screen/viewer-error-screen.html | 21 - .../viewer-error-screen/viewer-error-screen.js | 22 - .../pdf/elements/viewer-form-warning.html | 24 + .../resources/pdf/elements/viewer-form-warning.js | 29 + .../pdf/elements/viewer-form-warning/BUILD.gn | 18 - .../viewer-form-warning/viewer-form-warning.html | 24 - .../viewer-form-warning/viewer-form-warning.js | 29 - .../resources/pdf/elements/viewer-ink-host.html | 19 + .../resources/pdf/elements/viewer-ink-host.js | 332 +++++ .../pdf/elements/viewer-ink-host/BUILD.gn | 19 - .../elements/viewer-ink-host/viewer-ink-host.html | 19 - .../elements/viewer-ink-host/viewer-ink-host.js | 332 ----- .../pdf/elements/viewer-page-indicator.html | 48 + .../pdf/elements/viewer-page-indicator.js | 68 + .../pdf/elements/viewer-page-indicator/BUILD.gn | 18 - .../viewer-page-indicator.html | 48 - .../viewer-page-indicator/viewer-page-indicator.js | 68 - .../pdf/elements/viewer-page-selector.html | 56 + .../resources/pdf/elements/viewer-page-selector.js | 67 + .../pdf/elements/viewer-page-selector/BUILD.gn | 17 - .../viewer-page-selector/viewer-page-selector.html | 56 - .../viewer-page-selector/viewer-page-selector.js | 67 - .../pdf/elements/viewer-password-screen.html | 35 + .../pdf/elements/viewer-password-screen.js | 45 + .../pdf/elements/viewer-password-screen/BUILD.gn | 17 - .../viewer-password-screen.html | 35 - .../viewer-password-screen.js | 45 - .../resources/pdf/elements/viewer-pdf-toolbar.html | 289 ++++ .../resources/pdf/elements/viewer-pdf-toolbar.js | 265 ++++ .../pdf/elements/viewer-pdf-toolbar/BUILD.gn | 20 - .../viewer-pdf-toolbar/viewer-pdf-toolbar.html | 289 ---- .../viewer-pdf-toolbar/viewer-pdf-toolbar.js | 264 ---- .../resources/pdf/elements/viewer-pen-options.html | 90 ++ .../resources/pdf/elements/viewer-pen-options.js | 196 +++ .../pdf/elements/viewer-pen-options/BUILD.gn | 14 - .../viewer-pen-options/viewer-pen-options.html | 90 -- .../viewer-pen-options/viewer-pen-options.js | 195 --- .../pdf/elements/viewer-toolbar-dropdown.html | 109 ++ .../pdf/elements/viewer-toolbar-dropdown.js | 219 +++ .../pdf/elements/viewer-toolbar-dropdown/BUILD.gn | 15 - .../viewer-toolbar-dropdown.html | 109 -- .../viewer-toolbar-dropdown.js | 220 --- .../resources/pdf/elements/viewer-zoom-button.html | 85 ++ .../resources/pdf/elements/viewer-zoom-button.js | 134 ++ .../pdf/elements/viewer-zoom-toolbar.html | 73 + .../resources/pdf/elements/viewer-zoom-toolbar.js | 198 +++ .../pdf/elements/viewer-zoom-toolbar/BUILD.gn | 25 - .../viewer-zoom-toolbar/viewer-zoom-button.html | 85 -- .../viewer-zoom-toolbar/viewer-zoom-button.js | 134 -- .../viewer-zoom-toolbar/viewer-zoom-toolbar.html | 73 - .../viewer-zoom-toolbar/viewer-zoom-toolbar.js | 190 --- chromium/chrome/browser/resources/pdf/index.html | 17 +- chromium/chrome/browser/resources/pdf/ink/BUILD.gn | 3 + .../chrome/browser/resources/pdf/ink/ink_api.js | 11 +- .../resources/pdf/open_pdf_params_parser.js | 13 +- .../browser/resources/pdf/pdf_scripting_api.js | 185 +-- .../chrome/browser/resources/pdf/pdf_viewer.js | 1482 +++++++++----------- .../browser/resources/pdf/toolbar_manager.js | 147 +- chromium/chrome/browser/resources/pdf/viewport.js | 22 +- .../browser/resources/pdf/viewport_scroller.js | 83 +- 72 files changed, 4218 insertions(+), 3882 deletions(-) create mode 100644 chromium/chrome/browser/resources/pdf/annotation_tool.js create mode 100644 chromium/chrome/browser/resources/pdf/controller.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/BUILD.gn create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.js create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html create mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/BUILD.gn delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html delete mode 100644 chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js (limited to 'chromium/chrome/browser/resources/pdf') 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 + * }} + */ +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} + * @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} */ + 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.html b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.html new file mode 100644 index 00000000000..004aa1b1677 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.html @@ -0,0 +1,72 @@ + + + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.js b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.js new file mode 100644 index 00000000000..f25ebcef472 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark.js @@ -0,0 +1,140 @@ +// 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. + +/** + * The |title| is the text label displayed for the bookmark. + * + * 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, |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 + * structure. + * @typedef {{ + * title: string, + * page: (number | undefined), + * x: (number | undefined), + * y: (number | undefined), + * zoom: (number | undefined), + * uri: (string | undefined), + * children: !Array + * }} + */ +let Bookmark; + +(function() { +/** Amount that each level of bookmarks is indented by (px). */ +const BOOKMARK_INDENT = 20; + +Polymer({ + is: 'viewer-bookmark', + + properties: { + /** @type {Bookmark} */ + bookmark: { + type: Object, + observer: 'bookmarkChanged_', + }, + + depth: { + type: Number, + observer: 'depthChanged_' + }, + + /** @private */ + childDepth_: Number, + + /** @private */ + childrenShown_: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + + /** @type {?HTMLElement} The target for the key bindings below. */ + keyEventTarget: Object, + }, + + behaviors: [Polymer.IronA11yKeysBehavior], + + keyBindings: {'enter': 'onEnter_', 'space': 'onSpace_'}, + + /** @override */ + attached: function() { + this.keyEventTarget = this.$.item; + }, + + /** @private */ + bookmarkChanged_: function() { + this.$.expand.style.visibility = + this.bookmark.children.length > 0 ? 'visible' : 'hidden'; + }, + + /** @private */ + depthChanged_: function() { + this.childDepth_ = this.depth + 1; + this.$.item.style.paddingInlineStart = + (this.depth * BOOKMARK_INDENT) + 'px'; + }, + + /** @private */ + onClick_: function() { + 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: this.bookmark.x, + y: this.bookmark.y, + origin: 'bookmark' + }); + } else { + this.fire( + 'change-page', {page: this.bookmark.page, origin: 'bookmark'}); + } + } else if (this.bookmark.uri != null) { + this.fire('navigate', {uri: this.bookmark.uri, newtab: true}); + } + }, + + /** + * @param {!KeyboardEvent} e + * @private + */ + onEnter_: function(e) { + // Don't allow events which have propagated up from the expand button to + // trigger a click. + if (e.detail.keyboardEvent.target != this.$.expand) { + this.onClick_(); + } + }, + + /** + * @param {!KeyboardEvent} e + * @private + */ + onSpace_: function(e) { + // cr-icon-button stops propagation of space events, so there's no need + // to check the event source here. + this.onClick_(); + // Prevent default space scroll behavior. + e.detail.keyboardEvent.preventDefault(); + }, + + /** + * @param {!Event} e + * @private + */ + toggleChildren_: function(e) { + this.childrenShown_ = !this.childrenShown_; + e.stopPropagation(); // Prevent the above onClick_ handler from firing. + } +}); +})(); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-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-bookmark/viewer-bookmark.html b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html deleted file mode 100644 index 004aa1b1677..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js deleted file mode 100644 index f1cce48abaa..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * The |title| is the text label displayed for the bookmark. - * - * 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. - * - * |children| is an array of the |Bookmark|s that are below this in a table of - * contents tree - * structure. - * @typedef {{ - * title: string, - * page: (number | undefined), - * y: (number | undefined), - * uri: (string | undefined), - * children: !Array - * }} - */ -let Bookmark; - -(function() { -/** Amount that each level of bookmarks is indented by (px). */ -const BOOKMARK_INDENT = 20; - -Polymer({ - is: 'viewer-bookmark', - - properties: { - /** @type {Bookmark} */ - bookmark: { - type: Object, - observer: 'bookmarkChanged_', - }, - - depth: { - type: Number, - observer: 'depthChanged_' - }, - - /** @private */ - childDepth_: Number, - - /** @private */ - childrenShown_: { - type: Boolean, - reflectToAttribute: true, - value: false, - }, - - /** @type {?HTMLElement} The target for the key bindings below. */ - keyEventTarget: Object, - }, - - behaviors: [Polymer.IronA11yKeysBehavior], - - keyBindings: {'enter': 'onEnter_', 'space': 'onSpace_'}, - - /** @override */ - attached: function() { - this.keyEventTarget = this.$.item; - }, - - /** @private */ - bookmarkChanged_: function() { - this.$.expand.style.visibility = - this.bookmark.children.length > 0 ? 'visible' : 'hidden'; - }, - - /** @private */ - depthChanged_: function() { - this.childDepth_ = this.depth + 1; - this.$.item.style.paddingInlineStart = - (this.depth * BOOKMARK_INDENT) + 'px'; - }, - - /** @private */ - onClick_: function() { - if (this.bookmark.hasOwnProperty('page')) { - if (this.bookmark.hasOwnProperty('y')) { - this.fire('change-page-and-xy', { - page: this.bookmark.page, - x: 0, - y: this.bookmark.y, - origin: 'bookmark' - }); - } else { - this.fire( - 'change-page', {page: this.bookmark.page, origin: 'bookmark'}); - } - } else if (this.bookmark.hasOwnProperty('uri')) { - this.fire('navigate', {uri: this.bookmark.uri, newtab: true}); - } - }, - - /** - * @param {!KeyboardEvent} e - * @private - */ - onEnter_: function(e) { - // Don't allow events which have propagated up from the expand button to - // trigger a click. - if (e.detail.keyboardEvent.target != this.$.expand) { - this.onClick_(); - } - }, - - /** - * @param {!KeyboardEvent} e - * @private - */ - onSpace_: function(e) { - // cr-icon-button stops propagation of space events, so there's no need - // to check the event source here. - this.onClick_(); - // Prevent default space scroll behavior. - e.detail.keyboardEvent.preventDefault(); - }, - - /** - * @param {!Event} e - * @private - */ - toggleChildren_: function(e) { - this.childrenShown_ = !this.childrenShown_; - e.stopPropagation(); // Prevent the above onClick_ handler from firing. - } -}); -})(); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.html b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.html new file mode 100644 index 00000000000..25737d45127 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.html @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.js b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.js new file mode 100644 index 00000000000..082220a4c92 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen.js @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Polymer({ + is: 'viewer-error-screen', + properties: { + reloadFn: Function, + + strings: Object, + }, + + show: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + }, + + reload: function() { + if (this.reloadFn) { + this.reloadFn(); + } + } +}); 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-error-screen/viewer-error-screen.html b/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html deleted file mode 100644 index 25737d45127..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - 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/viewer-error-screen.js deleted file mode 100644 index 082220a4c92..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -Polymer({ - is: 'viewer-error-screen', - properties: { - reloadFn: Function, - - strings: Object, - }, - - show: function() { - /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); - }, - - reload: function() { - if (this.reloadFn) { - this.reloadFn(); - } - } -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.html b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.html new file mode 100644 index 00000000000..1e9e1ee4f67 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.html @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.js b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.js new file mode 100644 index 00000000000..539eddd6b67 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning.js @@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Polymer({ + is: 'viewer-form-warning', + properties: { + strings: Object, + }, + + /** @private {PromiseResolver} */ + resolver_: null, + + show: function() { + this.resolver_ = new PromiseResolver(); + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + return this.resolver_.promise; + }, + + onCancel: function() { + this.resolver_.reject(); + this.$.dialog.cancel(); + }, + + onAction: function() { + this.resolver_.resolve(); + this.$.dialog.close(); + }, +}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-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-form-warning/viewer-form-warning.html b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html deleted file mode 100644 index 1e9e1ee4f67..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js b/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js deleted file mode 100644 index 539eddd6b67..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js +++ /dev/null @@ -1,29 +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. - -Polymer({ - is: 'viewer-form-warning', - properties: { - strings: Object, - }, - - /** @private {PromiseResolver} */ - resolver_: null, - - show: function() { - this.resolver_ = new PromiseResolver(); - /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); - return this.resolver_.promise; - }, - - onCancel: function() { - this.resolver_.reject(); - this.$.dialog.cancel(); - }, - - onAction: function() { - this.resolver_.resolve(); - this.$.dialog.close(); - }, -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.html b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.html new file mode 100644 index 00000000000..1a7a2ff73ea --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.html @@ -0,0 +1,19 @@ + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.js b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.js new file mode 100644 index 00000000000..285edf8a35d --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host.js @@ -0,0 +1,332 @@ +// 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. + +/** @enum {string} */ +const State = { + LOADING: 'loading', + ACTIVE: 'active', + IDLE: 'idle' +}; + +const BACKGROUND_COLOR = '#525659'; + +/** + * Hosts the Ink component which is responsible for both PDF rendering and + * annotation when in annotation mode. + */ +Polymer({ + is: 'viewer-ink-host', + + /** @private {InkAPI} */ + ink_: null, + + /** @private {?string} */ + fileName_: null, + + /** @private {ArrayBuffer} */ + buffer_: null, + + /** @private {State} */ + state_: State.IDLE, + + /** @private {PointerEvent} */ + activePointer_: null, + + /** @private {?number} */ + lastZoom_: null, + + /** + * Used to conditionally allow a 'touchstart' event to cause + * a gesture. If we receive a 'touchstart' with this timestamp + * we will skip calling `preventDefault()`. + * @private {?number} + */ + allowTouchStartTimeStamp_: null, + + /** @private {boolean} */ + penMode_: false, + + /** @type {?Viewport} */ + viewport: null, + + /** @type {?AnnotationTool} */ + tool_: null, + + /** + * Whether we should suppress pointer events due to a gesture, + * eg. pinch-zoom. + * + * @private {boolean} + */ + pointerGesture_: false, + + listeners: { + pointerdown: 'onPointerDown_', + pointerup: 'onPointerUpOrCancel_', + pointermove: 'onPointerMove_', + pointercancel: 'onPointerUpOrCancel_', + pointerleave: 'onPointerLeave_', + touchstart: 'onTouchStart_', + }, + + /** Turns off pen mode if it is active. */ + resetPenMode() { + this.penMode_ = false; + }, + + /** @param {AnnotationTool} tool */ + setAnnotationTool(tool) { + this.tool_ = tool; + if (this.state_ == State.ACTIVE) { + this.ink_.setAnnotationTool(tool); + } + }, + + /** @param {PointerEvent} e */ + isActivePointer_: function(e) { + return this.activePointer_ && this.activePointer_.pointerId == e.pointerId; + }, + + /** + * Dispatches a pointer event to Ink. + * + * @param {PointerEvent} e + */ + dispatchPointerEvent_: function(e) { + // TODO(dstockwell) come up with a solution to propagate e.timeStamp. + this.ink_.dispatchPointerEvent(e.type, { + pointerId: e.pointerId, + pointerType: e.pointerType, + clientX: e.clientX, + clientY: e.clientY, + pressure: e.pressure, + buttons: e.buttons, + }); + }, + + /** @param {TouchEvent} e */ + onTouchStart_: function(e) { + if (e.timeStamp !== this.allowTouchStartTimeStamp_) { + e.preventDefault(); + } + this.allowTouchStartTimeStamp_ = null; + }, + + /** @param {PointerEvent} e */ + onPointerDown_: function(e) { + if (e.pointerType == 'mouse' && e.buttons != 1 || this.pointerGesture_) { + return; + } + + if (e.pointerType == 'pen') { + this.penMode_ = true; + } + + if (this.activePointer_) { + if (this.activePointer_.pointerType == 'touch' && + e.pointerType == 'touch') { + // A multi-touch gesture has started with the active pointer. Cancel + // the active pointer and suppress further events until it is released. + this.pointerGesture_ = true; + this.ink_.dispatchPointerEvent('pointercancel', { + pointerId: this.activePointer_.pointerId, + pointerType: this.activePointer_.pointerType, + }); + } + return; + } + + if (!this.viewport.isPointInsidePage({x: e.clientX, y: e.clientY}) && + (e.pointerType == 'touch' || e.pointerType == 'pen')) { + // If a touch or pen is outside the page, we allow pan gestures to start. + this.allowTouchStartTimeStamp_ = e.timeStamp; + return; + } + + if (e.pointerType == 'touch' && this.penMode_) { + // If we see a touch after having seen a pen, we allow touches to start + // pan gestures anywhere and suppress all touches from drawing. + this.allowTouchStartTimeStamp_ = e.timeStamp; + return; + } + + this.activePointer_ = e; + this.dispatchPointerEvent_(e); + }, + + /** @param {PointerEvent} e */ + onPointerLeave_: function(e) { + if (e.pointerType != 'mouse' || !this.isActivePointer_(e)) { + return; + } + this.onPointerUpOrCancel_(new PointerEvent('pointerup', e)); + }, + + /** @param {PointerEvent} e */ + onPointerUpOrCancel_: function(e) { + if (!this.isActivePointer_(e)) { + return; + } + this.activePointer_ = null; + if (!this.pointerGesture_) { + this.dispatchPointerEvent_(e); + // If the stroke was not cancelled (type == pointercanel), + // notify about mutation and record metrics. + if (e.type == 'pointerup') { + this.dispatchEvent(new CustomEvent('stroke-added')); + if (e.pointerType == 'mouse') { + PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_MOUSE); + } else if (e.pointerType == 'pen') { + PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_PEN); + } else if (e.pointerType == 'touch') { + PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_TOUCH); + } + if (this.tool_.tool == 'eraser') { + PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_ERASER); + } else if (this.tool_.tool == 'pen') { + PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_PEN); + } else if (this.tool_.tool == 'highlighter') { + PDFMetrics.record( + PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_HIGHLIGHTER); + } + } + } + this.pointerGesture_ = false; + }, + + /** @param {PointerEvent} e */ + onPointerMove_: function(e) { + if (!this.isActivePointer_(e) || this.pointerGesture_) { + return; + } + + let events = e.getCoalescedEvents(); + if (events.length == 0) { + events = [e]; + } + for (const event of events) { + this.dispatchPointerEvent_(event); + } + }, + + /** + * Begins annotation mode with the document represented by `data`. + * When the return value resolves the Ink component will be ready + * to render immediately. + * + * @param {string} fileName The name of the PDF file. + * @param {!ArrayBuffer} data The contents of the PDF document. + * @return {!Promise} void value. + */ + load: async function(fileName, data) { + this.fileName_ = fileName; + this.state_ = State.LOADING; + this.$.frame.src = 'ink/index.html'; + await new Promise(resolve => this.$.frame.onload = resolve); + this.ink_ = await this.$.frame.contentWindow.initInk(); + this.ink_.addUndoStateListener( + e => this.dispatchEvent( + new CustomEvent('undo-state-changed', {detail: e}))); + this.ink_.setPDF(data); + this.state_ = State.ACTIVE; + this.viewportChanged(); + // Wait for the next task to avoid a race where Ink drops the background + // color. + await new Promise(resolve => setTimeout(resolve)); + this.ink_.setOutOfBoundsColor(BACKGROUND_COLOR); + const spacing = Viewport.PAGE_SHADOW.top + Viewport.PAGE_SHADOW.bottom; + this.ink_.setPageSpacing(spacing); + this.style.visibility = 'visible'; + }, + + viewportChanged: function() { + if (this.state_ != State.ACTIVE) { + return; + } + const viewport = this.viewport; + const pos = viewport.position; + const size = viewport.size; + const zoom = viewport.getZoom(); + const documentWidth = viewport.getDocumentDimensions().width * zoom; + // Adjust for page shadows. + const y = pos.y - Viewport.PAGE_SHADOW.top * zoom; + let x = pos.x - Viewport.PAGE_SHADOW.left * zoom; + // Center the document if the width is smaller than the viewport. + if (documentWidth < size.width) { + x += (documentWidth - size.width) / 2; + } + // Invert the Y-axis and convert Pixels to Points. + const pixelsToPoints = 72 / 96; + const scale = pixelsToPoints / zoom; + const camera = { + top: (-y) * scale, + left: (x) * scale, + right: (x + size.width) * scale, + bottom: (-y - size.height) * scale, + }; + // Ink doesn't scale the shadow, so we must update it each time the zoom + // changes. + if (this.lastZoom_ !== zoom) { + this.lastZoom_ = zoom; + this.updateShadow_(zoom); + } + this.ink_.setCamera(camera); + }, + + /** Undo the last edit action. */ + undo() { + this.ink_.undo(); + }, + + /** Redo the last undone edit action. */ + redo() { + this.ink_.redo(); + }, + + /** + * @return {!Promise<{fileName: string, dataToSave: ArrayBuffer}>} + * The serialized PDF document including any annotations that were made. + */ + saveDocument: async function() { + if (this.state_ == State.ACTIVE) { + this.buffer_ = await this.ink_.getPDFDestructive().buffer; + this.state_ = State.IDLE; + } + return { + fileName: /** @type {string} */ (this.fileName_), + dataToSave: this.buffer_, + }; + }, + + /** @param {number} zoom */ + updateShadow_(zoom) { + const boxWidth = (50 * zoom) |0; + const shadowWidth = (8 * zoom) |0; + const width = boxWidth + shadowWidth * 2 + 2; + const boxOffset = (width - boxWidth) / 2; + + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = width; + + const ctx = canvas.getContext('2d'); + ctx.fillStyle = 'black'; + ctx.shadowColor = 'black'; + ctx.shadowBlur = shadowWidth; + ctx.fillRect(boxOffset, boxOffset, boxWidth, boxWidth); + ctx.shadowBlur = 0; + + // 9-piece markers + for (let i = 0; i < 4; i++) { + ctx.fillStyle = 'white'; + ctx.fillRect(0, 0, width, 1); + ctx.fillStyle = 'black'; + ctx.fillRect(shadowWidth + 1, 0, boxWidth, 1); + ctx.rotate(0.5 * Math.PI); + ctx.translate(0, -width); + } + + this.ink_.setBorderImage(canvas.toDataURL()); + } +}); 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-ink-host/viewer-ink-host.html b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html deleted file mode 100644 index 1a7a2ff73ea..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js b/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js deleted file mode 100644 index 285edf8a35d..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js +++ /dev/null @@ -1,332 +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. - -/** @enum {string} */ -const State = { - LOADING: 'loading', - ACTIVE: 'active', - IDLE: 'idle' -}; - -const BACKGROUND_COLOR = '#525659'; - -/** - * Hosts the Ink component which is responsible for both PDF rendering and - * annotation when in annotation mode. - */ -Polymer({ - is: 'viewer-ink-host', - - /** @private {InkAPI} */ - ink_: null, - - /** @private {?string} */ - fileName_: null, - - /** @private {ArrayBuffer} */ - buffer_: null, - - /** @private {State} */ - state_: State.IDLE, - - /** @private {PointerEvent} */ - activePointer_: null, - - /** @private {?number} */ - lastZoom_: null, - - /** - * Used to conditionally allow a 'touchstart' event to cause - * a gesture. If we receive a 'touchstart' with this timestamp - * we will skip calling `preventDefault()`. - * @private {?number} - */ - allowTouchStartTimeStamp_: null, - - /** @private {boolean} */ - penMode_: false, - - /** @type {?Viewport} */ - viewport: null, - - /** @type {?AnnotationTool} */ - tool_: null, - - /** - * Whether we should suppress pointer events due to a gesture, - * eg. pinch-zoom. - * - * @private {boolean} - */ - pointerGesture_: false, - - listeners: { - pointerdown: 'onPointerDown_', - pointerup: 'onPointerUpOrCancel_', - pointermove: 'onPointerMove_', - pointercancel: 'onPointerUpOrCancel_', - pointerleave: 'onPointerLeave_', - touchstart: 'onTouchStart_', - }, - - /** Turns off pen mode if it is active. */ - resetPenMode() { - this.penMode_ = false; - }, - - /** @param {AnnotationTool} tool */ - setAnnotationTool(tool) { - this.tool_ = tool; - if (this.state_ == State.ACTIVE) { - this.ink_.setAnnotationTool(tool); - } - }, - - /** @param {PointerEvent} e */ - isActivePointer_: function(e) { - return this.activePointer_ && this.activePointer_.pointerId == e.pointerId; - }, - - /** - * Dispatches a pointer event to Ink. - * - * @param {PointerEvent} e - */ - dispatchPointerEvent_: function(e) { - // TODO(dstockwell) come up with a solution to propagate e.timeStamp. - this.ink_.dispatchPointerEvent(e.type, { - pointerId: e.pointerId, - pointerType: e.pointerType, - clientX: e.clientX, - clientY: e.clientY, - pressure: e.pressure, - buttons: e.buttons, - }); - }, - - /** @param {TouchEvent} e */ - onTouchStart_: function(e) { - if (e.timeStamp !== this.allowTouchStartTimeStamp_) { - e.preventDefault(); - } - this.allowTouchStartTimeStamp_ = null; - }, - - /** @param {PointerEvent} e */ - onPointerDown_: function(e) { - if (e.pointerType == 'mouse' && e.buttons != 1 || this.pointerGesture_) { - return; - } - - if (e.pointerType == 'pen') { - this.penMode_ = true; - } - - if (this.activePointer_) { - if (this.activePointer_.pointerType == 'touch' && - e.pointerType == 'touch') { - // A multi-touch gesture has started with the active pointer. Cancel - // the active pointer and suppress further events until it is released. - this.pointerGesture_ = true; - this.ink_.dispatchPointerEvent('pointercancel', { - pointerId: this.activePointer_.pointerId, - pointerType: this.activePointer_.pointerType, - }); - } - return; - } - - if (!this.viewport.isPointInsidePage({x: e.clientX, y: e.clientY}) && - (e.pointerType == 'touch' || e.pointerType == 'pen')) { - // If a touch or pen is outside the page, we allow pan gestures to start. - this.allowTouchStartTimeStamp_ = e.timeStamp; - return; - } - - if (e.pointerType == 'touch' && this.penMode_) { - // If we see a touch after having seen a pen, we allow touches to start - // pan gestures anywhere and suppress all touches from drawing. - this.allowTouchStartTimeStamp_ = e.timeStamp; - return; - } - - this.activePointer_ = e; - this.dispatchPointerEvent_(e); - }, - - /** @param {PointerEvent} e */ - onPointerLeave_: function(e) { - if (e.pointerType != 'mouse' || !this.isActivePointer_(e)) { - return; - } - this.onPointerUpOrCancel_(new PointerEvent('pointerup', e)); - }, - - /** @param {PointerEvent} e */ - onPointerUpOrCancel_: function(e) { - if (!this.isActivePointer_(e)) { - return; - } - this.activePointer_ = null; - if (!this.pointerGesture_) { - this.dispatchPointerEvent_(e); - // If the stroke was not cancelled (type == pointercanel), - // notify about mutation and record metrics. - if (e.type == 'pointerup') { - this.dispatchEvent(new CustomEvent('stroke-added')); - if (e.pointerType == 'mouse') { - PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_MOUSE); - } else if (e.pointerType == 'pen') { - PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_PEN); - } else if (e.pointerType == 'touch') { - PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_DEVICE_TOUCH); - } - if (this.tool_.tool == 'eraser') { - PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_ERASER); - } else if (this.tool_.tool == 'pen') { - PDFMetrics.record(PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_PEN); - } else if (this.tool_.tool == 'highlighter') { - PDFMetrics.record( - PDFMetrics.UserAction.ANNOTATE_STROKE_TOOL_HIGHLIGHTER); - } - } - } - this.pointerGesture_ = false; - }, - - /** @param {PointerEvent} e */ - onPointerMove_: function(e) { - if (!this.isActivePointer_(e) || this.pointerGesture_) { - return; - } - - let events = e.getCoalescedEvents(); - if (events.length == 0) { - events = [e]; - } - for (const event of events) { - this.dispatchPointerEvent_(event); - } - }, - - /** - * Begins annotation mode with the document represented by `data`. - * When the return value resolves the Ink component will be ready - * to render immediately. - * - * @param {string} fileName The name of the PDF file. - * @param {!ArrayBuffer} data The contents of the PDF document. - * @return {!Promise} void value. - */ - load: async function(fileName, data) { - this.fileName_ = fileName; - this.state_ = State.LOADING; - this.$.frame.src = 'ink/index.html'; - await new Promise(resolve => this.$.frame.onload = resolve); - this.ink_ = await this.$.frame.contentWindow.initInk(); - this.ink_.addUndoStateListener( - e => this.dispatchEvent( - new CustomEvent('undo-state-changed', {detail: e}))); - this.ink_.setPDF(data); - this.state_ = State.ACTIVE; - this.viewportChanged(); - // Wait for the next task to avoid a race where Ink drops the background - // color. - await new Promise(resolve => setTimeout(resolve)); - this.ink_.setOutOfBoundsColor(BACKGROUND_COLOR); - const spacing = Viewport.PAGE_SHADOW.top + Viewport.PAGE_SHADOW.bottom; - this.ink_.setPageSpacing(spacing); - this.style.visibility = 'visible'; - }, - - viewportChanged: function() { - if (this.state_ != State.ACTIVE) { - return; - } - const viewport = this.viewport; - const pos = viewport.position; - const size = viewport.size; - const zoom = viewport.getZoom(); - const documentWidth = viewport.getDocumentDimensions().width * zoom; - // Adjust for page shadows. - const y = pos.y - Viewport.PAGE_SHADOW.top * zoom; - let x = pos.x - Viewport.PAGE_SHADOW.left * zoom; - // Center the document if the width is smaller than the viewport. - if (documentWidth < size.width) { - x += (documentWidth - size.width) / 2; - } - // Invert the Y-axis and convert Pixels to Points. - const pixelsToPoints = 72 / 96; - const scale = pixelsToPoints / zoom; - const camera = { - top: (-y) * scale, - left: (x) * scale, - right: (x + size.width) * scale, - bottom: (-y - size.height) * scale, - }; - // Ink doesn't scale the shadow, so we must update it each time the zoom - // changes. - if (this.lastZoom_ !== zoom) { - this.lastZoom_ = zoom; - this.updateShadow_(zoom); - } - this.ink_.setCamera(camera); - }, - - /** Undo the last edit action. */ - undo() { - this.ink_.undo(); - }, - - /** Redo the last undone edit action. */ - redo() { - this.ink_.redo(); - }, - - /** - * @return {!Promise<{fileName: string, dataToSave: ArrayBuffer}>} - * The serialized PDF document including any annotations that were made. - */ - saveDocument: async function() { - if (this.state_ == State.ACTIVE) { - this.buffer_ = await this.ink_.getPDFDestructive().buffer; - this.state_ = State.IDLE; - } - return { - fileName: /** @type {string} */ (this.fileName_), - dataToSave: this.buffer_, - }; - }, - - /** @param {number} zoom */ - updateShadow_(zoom) { - const boxWidth = (50 * zoom) |0; - const shadowWidth = (8 * zoom) |0; - const width = boxWidth + shadowWidth * 2 + 2; - const boxOffset = (width - boxWidth) / 2; - - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = width; - - const ctx = canvas.getContext('2d'); - ctx.fillStyle = 'black'; - ctx.shadowColor = 'black'; - ctx.shadowBlur = shadowWidth; - ctx.fillRect(boxOffset, boxOffset, boxWidth, boxWidth); - ctx.shadowBlur = 0; - - // 9-piece markers - for (let i = 0; i < 4; i++) { - ctx.fillStyle = 'white'; - ctx.fillRect(0, 0, width, 1); - ctx.fillStyle = 'black'; - ctx.fillRect(shadowWidth + 1, 0, boxWidth, 1); - ctx.rotate(0.5 * Math.PI); - ctx.translate(0, -width); - } - - this.ink_.setBorderImage(canvas.toDataURL()); - } -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.html new file mode 100644 index 00000000000..70ad2578685 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.html @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.js b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.js new file mode 100644 index 00000000000..50ad4f4cb49 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator.js @@ -0,0 +1,68 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Polymer({ + is: 'viewer-page-indicator', + + properties: { + label: {type: String, value: '1'}, + + index: {type: Number, observer: 'indexChanged'}, + + pageLabels: {type: Array, value: null, observer: 'pageLabelsChanged'} + }, + + /** @type {number|undefined} */ + timerId: undefined, + + /** @override */ + ready: function() { + const callback = this.fadeIn_.bind(this); + window.addEventListener('scroll', function() { + requestAnimationFrame(callback); + }); + }, + + /** @private */ + fadeIn_: function() { + const percent = window.scrollY / + (document.scrollingElement.scrollHeight - + document.documentElement.clientHeight); + this.style.top = + percent * (document.documentElement.clientHeight - this.offsetHeight) + + 'px'; + // + // If overlay scrollbars are enabled, prevent them from overlapping the + // triangle. TODO(dbeam): various platforms can enable overlay scrolling, + // not just Mac. The scrollbars seem to have different widths/appearances on + // those platforms, though. + assert(document.documentElement.dir); + const endEdge = isRTL() ? 'left' : 'right'; + if (window.innerWidth == document.scrollingElement.scrollWidth) { + this.style[endEdge] = '16px'; + } else { + this.style[endEdge] = '0px'; + } + // + this.style.opacity = 1; + clearTimeout(this.timerId); + + this.timerId = setTimeout(() => { + this.style.opacity = 0; + this.timerId = undefined; + }, 2000); + }, + + pageLabelsChanged: function() { + this.indexChanged(); + }, + + indexChanged: function() { + if (this.pageLabels) { + this.label = this.pageLabels[this.index]; + } else { + this.label = String(this.index + 1); + } + } +}); diff --git a/chromium/chrome/browser/resources/pdf/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-indicator/viewer-page-indicator.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.html deleted file mode 100644 index 70ad2578685..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - 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/viewer-page-indicator.js deleted file mode 100644 index 50ad4f4cb49..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -Polymer({ - is: 'viewer-page-indicator', - - properties: { - label: {type: String, value: '1'}, - - index: {type: Number, observer: 'indexChanged'}, - - pageLabels: {type: Array, value: null, observer: 'pageLabelsChanged'} - }, - - /** @type {number|undefined} */ - timerId: undefined, - - /** @override */ - ready: function() { - const callback = this.fadeIn_.bind(this); - window.addEventListener('scroll', function() { - requestAnimationFrame(callback); - }); - }, - - /** @private */ - fadeIn_: function() { - const percent = window.scrollY / - (document.scrollingElement.scrollHeight - - document.documentElement.clientHeight); - this.style.top = - percent * (document.documentElement.clientHeight - this.offsetHeight) + - 'px'; - // - // If overlay scrollbars are enabled, prevent them from overlapping the - // triangle. TODO(dbeam): various platforms can enable overlay scrolling, - // not just Mac. The scrollbars seem to have different widths/appearances on - // those platforms, though. - assert(document.documentElement.dir); - const endEdge = isRTL() ? 'left' : 'right'; - if (window.innerWidth == document.scrollingElement.scrollWidth) { - this.style[endEdge] = '16px'; - } else { - this.style[endEdge] = '0px'; - } - // - this.style.opacity = 1; - clearTimeout(this.timerId); - - this.timerId = setTimeout(() => { - this.style.opacity = 0; - this.timerId = undefined; - }, 2000); - }, - - pageLabelsChanged: function() { - this.indexChanged(); - }, - - indexChanged: function() { - if (this.pageLabels) { - this.label = this.pageLabels[this.index]; - } else { - this.label = String(this.index + 1); - } - } -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.html new file mode 100644 index 00000000000..e1fdc869efe --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.html @@ -0,0 +1,56 @@ + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.js b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.js new file mode 100644 index 00000000000..b7999237a98 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector.js @@ -0,0 +1,67 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Polymer({ + is: 'viewer-page-selector', + + properties: { + /** + * The number of pages the document contains. + */ + docLength: {type: Number, value: 1, observer: 'docLengthChanged_'}, + + /** + * The current page being viewed (1-based). A change to pageNo is mirrored + * immediately to the input field. A change to the input field is not + * mirrored back until pageNoCommitted() is called and change-page is fired. + */ + pageNo: { + type: Number, + value: 1, + }, + + strings: Object, + }, + + /** @return {!CrInputElement} */ + get pageSelector() { + return this.$.pageselector; + }, + + pageNoCommitted: function() { + const page = parseInt(this.pageSelector.value, 10); + + if (!isNaN(page) && page <= this.docLength && page > 0) { + this.fire('change-page', {page: page - 1, origin: 'pageselector'}); + } else { + this.pageSelector.value = this.pageNo.toString(); + } + this.pageSelector.blur(); + }, + + /** @private */ + docLengthChanged_: function() { + const numDigits = this.docLength.toString().length; + this.style.setProperty('--page-length-digits', `${numDigits}`); + }, + + select: function() { + this.pageSelector.select(); + }, + + /** + * @return {boolean} True if the selector input field is currently focused. + */ + isActive: function() { + return this.shadowRoot.activeElement == this.pageSelector; + }, + + /** + * Immediately remove any non-digit characters. + * @private + */ + onInputValueChange_: function() { + this.pageSelector.value = this.pageSelector.value.replace(/[^\d]/, ''); + }, +}); 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-page-selector/viewer-page-selector.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html deleted file mode 100644 index e1fdc869efe..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - 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/viewer-page-selector.js deleted file mode 100644 index b7999237a98..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.js +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -Polymer({ - is: 'viewer-page-selector', - - properties: { - /** - * The number of pages the document contains. - */ - docLength: {type: Number, value: 1, observer: 'docLengthChanged_'}, - - /** - * The current page being viewed (1-based). A change to pageNo is mirrored - * immediately to the input field. A change to the input field is not - * mirrored back until pageNoCommitted() is called and change-page is fired. - */ - pageNo: { - type: Number, - value: 1, - }, - - strings: Object, - }, - - /** @return {!CrInputElement} */ - get pageSelector() { - return this.$.pageselector; - }, - - pageNoCommitted: function() { - const page = parseInt(this.pageSelector.value, 10); - - if (!isNaN(page) && page <= this.docLength && page > 0) { - this.fire('change-page', {page: page - 1, origin: 'pageselector'}); - } else { - this.pageSelector.value = this.pageNo.toString(); - } - this.pageSelector.blur(); - }, - - /** @private */ - docLengthChanged_: function() { - const numDigits = this.docLength.toString().length; - this.style.setProperty('--page-length-digits', `${numDigits}`); - }, - - select: function() { - this.pageSelector.select(); - }, - - /** - * @return {boolean} True if the selector input field is currently focused. - */ - isActive: function() { - return this.shadowRoot.activeElement == this.pageSelector; - }, - - /** - * Immediately remove any non-digit characters. - * @private - */ - onInputValueChange_: function() { - this.pageSelector.value = this.pageSelector.value.replace(/[^\d]/, ''); - }, -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.html b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.html new file mode 100644 index 00000000000..aebc16bf138 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.js b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.js new file mode 100644 index 00000000000..5506ad2915c --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen.js @@ -0,0 +1,45 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Polymer({ + is: 'viewer-password-screen', + + properties: { + invalid: Boolean, + + strings: Object, + }, + + get active() { + return this.$.dialog.open; + }, + + show: function() { + this.$.dialog.showModal(); + }, + + close: function() { + if (this.active) { + this.$.dialog.close(); + } + }, + + deny: function() { + const password = /** @type {!CrInputElement} */ (this.$.password); + password.disabled = false; + this.$.submit.disabled = false; + this.invalid = true; + password.select(); + }, + + submit: function() { + const password = /** @type {!CrInputElement} */ (this.$.password); + if (password.value.length == 0) { + return; + } + password.disabled = true; + this.$.submit.disabled = true; + this.fire('password-submitted', {password: password.value}); + }, +}); 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-password-screen/viewer-password-screen.html b/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.html deleted file mode 100644 index aebc16bf138..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - 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/viewer-password-screen.js deleted file mode 100644 index 5506ad2915c..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-password-screen/viewer-password-screen.js +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -Polymer({ - is: 'viewer-password-screen', - - properties: { - invalid: Boolean, - - strings: Object, - }, - - get active() { - return this.$.dialog.open; - }, - - show: function() { - this.$.dialog.showModal(); - }, - - close: function() { - if (this.active) { - this.$.dialog.close(); - } - }, - - deny: function() { - const password = /** @type {!CrInputElement} */ (this.$.password); - password.disabled = false; - this.$.submit.disabled = false; - this.invalid = true; - password.select(); - }, - - submit: function() { - const password = /** @type {!CrInputElement} */ (this.$.password); - if (password.value.length == 0) { - return; - } - password.disabled = true; - this.$.submit.disabled = true; - this.fire('password-submitted', {password: password.value}); - }, -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html new file mode 100644 index 00000000000..348e2215fed --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js new file mode 100644 index 00000000000..07407cfee75 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js @@ -0,0 +1,265 @@ +// 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', + + properties: { + /** + * Whether annotation mode can be entered. This would be false if for + * example the PDF is encrypted or password protected. Note, this is + * true regardless of whether the feature flag is enabled. + */ + annotationAvailable: { + type: Boolean, + value: true, + }, + + /** Whether the viewer is currently in annotation mode. */ + annotationMode: { + type: Boolean, + notify: true, + value: false, + reflectToAttribute: true, + }, + + /** @type {?AnnotationTool} */ + annotationTool: { + type: Object, + value: null, + notify: true, + }, + + /** + * Tree of PDF bookmarks (empty if the document has no bookmarks). + * @type {!Array} + */ + bookmarks: { + type: Array, + value: () => [], + }, + + canRedoAnnotation: { + type: Boolean, + value: false, + }, + + canUndoAnnotation: { + type: Boolean, + value: false, + }, + + /** The number of pages in the PDF document. */ + docLength: Number, + + /** The title of the PDF document. */ + docTitle: String, + + /** The current loading progress of the PDF document (0 - 100). */ + loadProgress: { + type: Number, + observer: 'loadProgressChanged_', + }, + + /** Whether the toolbar is opened and visible. */ + opened: { + type: Boolean, + value: true, + }, + + /** The number of the page being viewed (1-based). */ + pageNo: Number, + + /** Whether the PDF Annotations feature is enabled. */ + pdfAnnotationsEnabled: { + type: Boolean, + value: false, + }, + + /** Whether the Printing feature is enabled. */ + printingEnabled: { + type: Boolean, + value: false, + }, + + strings: Object, + }, + + /** @type {?Object} */ + animation_: null, + + /** + * @param {number} newProgress + * @param {number} oldProgress + * @private + */ + loadProgressChanged_: function(newProgress, oldProgress) { + const loaded = newProgress >= 100; + const progressReset = newProgress < oldProgress; + if (progressReset || loaded) { + this.$.pageselector.classList.toggle('invisible', !loaded); + this.$.buttons.classList.toggle('invisible', !loaded); + this.$.progress.style.opacity = loaded ? 0 : 1; + this.$['annotations-bar'].hidden = !loaded || !this.annotationMode; + } + }, + + hide: function() { + if (this.opened && !this.shouldKeepOpen()) { + this.toggleVisibility(); + } + }, + + show: function() { + if (!this.opened) { + this.toggleVisibility(); + } + }, + + toggleVisibility: function() { + this.opened = !this.opened; + + // We keep a handle on the animation in order to cancel the filling + // behavior of previous animations. + if (this.animation_) { + this.animation_.cancel(); + } + + if (this.opened) { + this.animation_ = this.animate( + [{transform: 'translateY(-100%)'}, {transform: 'translateY(0%)'}], { + duration: 250, + easing: 'cubic-bezier(0, 0, 0.2, 1)', + fill: 'forwards', + }); + } else { + this.animation_ = this.animate( + [{transform: 'translateY(0%)'}, {transform: 'translateY(-100%)'}], { + duration: 250, + easing: 'cubic-bezier(0.4, 0, 1, 1)', + fill: 'forwards', + }); + } + }, + + selectPageNumber: function() { + this.$.pageselector.select(); + }, + + /** @return {boolean} Whether the toolbar should be kept open. */ + shouldKeepOpen: function() { + return this.$.bookmarks.dropdownOpen || this.loadProgress < 100 || + this.$.pageselector.isActive() || this.annotationMode; + }, + + /** @return {boolean} Whether a dropdown was open and was hidden. */ + hideDropdowns: function() { + let result = false; + if (this.$.bookmarks.dropdownOpen) { + this.$.bookmarks.toggleDropdown(); + result = true; + } + if (this.$.pen.dropdownOpen) { + this.$.pen.toggleDropdown(); + result = true; + } + if (this.$.highlighter.dropdownOpen) { + this.$.highlighter.toggleDropdown(); + result = true; + } + return result; + }, + + /** @param {number} lowerBound */ + setDropdownLowerBound: function(lowerBound) { + this.$.bookmarks.lowerBound = lowerBound; + }, + + rotateRight: function() { + this.fire('rotate-right'); + }, + + download: function() { + this.fire('save'); + }, + + print: function() { + this.fire('print'); + }, + + undo: function() { + this.fire('undo'); + }, + + redo: function() { + this.fire('redo'); + }, + + toggleAnnotation: function() { + this.annotationMode = !this.annotationMode; + if (this.annotationMode) { + // Select pen tool when entering annotation mode. + this.updateAnnotationTool_(this.$.pen); + } + this.dispatchEvent(new CustomEvent('annotation-mode-toggled', { + detail: { + value: this.annotationMode, + }, + })); + }, + + /** + * @param {!Event} e + * @private + */ + annotationToolClicked_: function(e) { + this.updateAnnotationTool_(/** @type {!HTMLElement} */ (e.currentTarget)); + }, + + /** + * @param {!Event} e + * @private + */ + annotationToolOptionChanged_: function(e) { + const element = e.currentTarget.parentElement; + if (!this.annotationTool || element.id != this.annotationTool.tool) { + return; + } + this.updateAnnotationTool_(e.currentTarget.parentElement); + }, + + /** + * @param {!HTMLElement} element + * @private + */ + updateAnnotationTool_: function(element) { + const tool = element.id; + const options = element.querySelector('viewer-pen-options') || { + selectedSize: 1, + selectedColor: null, + }; + const attributeStyleMap = element.attributeStyleMap; + attributeStyleMap.set('--pen-tip-fill', options.selectedColor); + attributeStyleMap.set( + '--pen-tip-border', + options.selectedColor == '#000000' ? 'currentcolor' : + options.selectedColor); + this.annotationTool = { + tool: tool, + size: options.selectedSize, + color: options.selectedColor, + }; + }, + + /** + * @param {string} toolName + * @return {boolean} Whether the annotation tool is using tool |toolName|. + * @private + */ + isAnnotationTool_: function(toolName) { + return !!this.annotationTool && this.annotationTool.tool === toolName; + }, +}); +})(); 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-pdf-toolbar/viewer-pdf-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html deleted file mode 100644 index e183debc86d..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js deleted file mode 100644 index afb87dfe6b7..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -(function() { -Polymer({ - is: 'viewer-pdf-toolbar', - - properties: { - /** - * Whether annotation mode can be entered. This would be false if for - * example the PDF is encrypted or password protected. Note, this is - * true regardless of whether the feature flag is enabled. - */ - annotationAvailable: { - type: Boolean, - value: true, - }, - - /** Whether the viewer is currently in annotation mode. */ - annotationMode: { - type: Boolean, - notify: true, - value: false, - reflectToAttribute: true, - }, - - /** @type {?Object} */ - annotationTool: { - type: Object, - value: null, - notify: true, - }, - - /** - * Tree of PDF bookmarks (empty if the document has no bookmarks). - * @type {!Array} - */ - bookmarks: { - type: Array, - value: () => [], - }, - - canRedoAnnotation: { - type: Boolean, - value: false, - }, - - canUndoAnnotation: { - type: Boolean, - value: false, - }, - - /** The number of pages in the PDF document. */ - docLength: Number, - - /** The title of the PDF document. */ - docTitle: String, - - /** The current loading progress of the PDF document (0 - 100). */ - loadProgress: { - type: Number, - observer: 'loadProgressChanged_', - }, - - /** Whether the toolbar is opened and visible. */ - opened: { - type: Boolean, - value: true, - }, - - /** The number of the page being viewed (1-based). */ - pageNo: Number, - - /** Whether the PDF Annotations feature is enabled. */ - pdfAnnotationsEnabled: { - type: Boolean, - value: false, - }, - - /** Whether the Printing feature is enabled. */ - printingEnabled: { - type: Boolean, - value: false, - }, - - strings: Object, - }, - - /** @type {?Object} */ - animation_: null, - - /** - * @param {number} newProgress - * @param {number} oldProgress - * @private - */ - loadProgressChanged_: function(newProgress, oldProgress) { - const loaded = newProgress >= 100; - const progressReset = newProgress < oldProgress; - if (progressReset || loaded) { - this.$.pageselector.classList.toggle('invisible', !loaded); - this.$.buttons.classList.toggle('invisible', !loaded); - this.$.progress.style.opacity = loaded ? 0 : 1; - this.$['annotations-bar'].hidden = !loaded || !this.annotationMode; - } - }, - - hide: function() { - if (this.opened && !this.shouldKeepOpen()) { - this.toggleVisibility(); - } - }, - - show: function() { - if (!this.opened) { - this.toggleVisibility(); - } - }, - - toggleVisibility: function() { - this.opened = !this.opened; - - // We keep a handle on the animation in order to cancel the filling - // behavior of previous animations. - if (this.animation_) { - this.animation_.cancel(); - } - - if (this.opened) { - this.animation_ = this.animate( - [{transform: 'translateY(-100%)'}, {transform: 'translateY(0%)'}], { - duration: 250, - easing: 'cubic-bezier(0, 0, 0.2, 1)', - fill: 'forwards', - }); - } else { - this.animation_ = this.animate( - [{transform: 'translateY(0%)'}, {transform: 'translateY(-100%)'}], { - duration: 250, - easing: 'cubic-bezier(0.4, 0, 1, 1)', - fill: 'forwards', - }); - } - }, - - selectPageNumber: function() { - this.$.pageselector.select(); - }, - - /** @return {boolean} Whether the toolbar should be kept open. */ - shouldKeepOpen: function() { - return this.$.bookmarks.dropdownOpen || this.loadProgress < 100 || - this.$.pageselector.isActive() || this.annotationMode; - }, - - /** @return {boolean} Whether a dropdown was open and was hidden. */ - hideDropdowns: function() { - let result = false; - if (this.$.bookmarks.dropdownOpen) { - this.$.bookmarks.toggleDropdown(); - result = true; - } - if (this.$.pen.dropdownOpen) { - this.$.pen.toggleDropdown(); - result = true; - } - if (this.$.highlighter.dropdownOpen) { - this.$.highlighter.toggleDropdown(); - result = true; - } - return result; - }, - - /** @param {number} lowerBound */ - setDropdownLowerBound: function(lowerBound) { - this.$.bookmarks.lowerBound = lowerBound; - }, - - rotateRight: function() { - this.fire('rotate-right'); - }, - - download: function() { - this.fire('save'); - }, - - print: function() { - this.fire('print'); - }, - - undo: function() { - this.fire('undo'); - }, - - redo: function() { - this.fire('redo'); - }, - - toggleAnnotation: function() { - this.annotationMode = !this.annotationMode; - if (this.annotationMode) { - // Select pen tool when entering annotation mode. - this.updateAnnotationTool_(this.$.pen); - } - this.dispatchEvent(new CustomEvent('annotation-mode-toggled', { - detail: { - value: this.annotationMode, - }, - })); - }, - - /** - * @param {!Event} e - * @private - */ - annotationToolClicked_: function(e) { - this.updateAnnotationTool_(/** @type {!HTMLElement} */ (e.currentTarget)); - }, - - /** - * @param {!Event} e - * @private - */ - annotationToolOptionChanged_: function(e) { - const element = e.currentTarget.parentElement; - if (!this.annotationTool || element.id != this.annotationTool.tool) { - return; - } - this.updateAnnotationTool_(e.currentTarget.parentElement); - }, - - /** - * @param {!HTMLElement} element - * @private - */ - updateAnnotationTool_: function(element) { - const tool = element.id; - const options = element.querySelector('viewer-pen-options') || { - selectedSize: 1, - selectedColor: null, - }; - const attributeStyleMap = element.attributeStyleMap; - attributeStyleMap.set('--pen-tip-fill', options.selectedColor); - attributeStyleMap.set( - '--pen-tip-border', - options.selectedColor == '#000000' ? 'currentcolor' : - options.selectedColor); - this.annotationTool = { - tool: tool, - size: options.selectedSize, - color: options.selectedColor, - }; - }, - - /** - * @param {string} toolName - * @return {boolean} Whether the annotation tool is using tool |toolName|. - * @private - */ - isAnnotationTool_: function(toolName) { - return !!this.annotationTool && this.annotationTool.tool === toolName; - }, -}); -})(); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.html new file mode 100644 index 00000000000..dbf18b0bfda --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.html @@ -0,0 +1,90 @@ + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.js new file mode 100644 index 00000000000..d63343404fb --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options.js @@ -0,0 +1,196 @@ +// 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. + +const colors = [ + // row 1 + {name: 'annotationColorBlack', color: '#000000'}, + {name: 'annotationColorRed', color: '#ff5252'}, + {name: 'annotationColorYellow', color: '#ffbc00'}, + {name: 'annotationColorGreen', color: '#00c853'}, + {name: 'annotationColorCyan', color: '#00b0ff'}, + {name: 'annotationColorPurple', color: '#d500f9'}, + {name: 'annotationColorBrown', color: '#8d6e63'}, + // row 2 + {name: 'annotationColorWhite', color: '#fafafa', outline: true}, + {name: 'annotationColorCrimson', color: '#a52714'}, + {name: 'annotationColorAmber', color: '#ee8100'}, + {name: 'annotationColorAvocadoGreen', color: '#558b2f'}, + {name: 'annotationColorCobaltBlue', color: '#01579b'}, + {name: 'annotationColorDeepPurple', color: '#8e24aa'}, + {name: 'annotationColorDarkBrown', color: '#4e342e'}, + // row 3 + {name: 'annotationColorDarkGrey', color: '#90a4ae'}, + {name: 'annotationColorHotPink', color: '#ff4081'}, + {name: 'annotationColorOrange', color: '#ff6e40'}, + {name: 'annotationColorLime', color: '#aeea00'}, + {name: 'annotationColorBlue', color: '#304ffe'}, + {name: 'annotationColorViolet', color: '#7c4dff'}, + {name: 'annotationColorTeal', color: '#1de9b6'}, + // row 4 + {name: 'annotationColorLightGrey', color: '#cfd8dc'}, + {name: 'annotationColorLightPink', color: '#f8bbd0'}, + {name: 'annotationColorLightOrange', color: '#ffccbc'}, + {name: 'annotationColorLightGreen', color: '#f0f4c3'}, + {name: 'annotationColorLightBlue', color: '#9fa8da'}, + {name: 'annotationColorLavender', color: '#d1c4e9'}, + {name: 'annotationColorLightTeal', color: '#b2dfdb'}, +]; + +const sizes = [ + {name: 'annotationSize1', size: 0}, + {name: 'annotationSize2', size: 0.1429}, + {name: 'annotationSize3', size: 0.2857}, + {name: 'annotationSize4', size: 0.4286}, + {name: 'annotationSize8', size: 0.5714}, + {name: 'annotationSize12', size: 0.7143}, + {name: 'annotationSize16', size: 0.8571}, + {name: 'annotationSize20', size: 1}, +]; + +/** + * Displays a set of radio buttons to select from + * a predefined list of colors and sizes. + */ +Polymer({ + is: 'viewer-pen-options', + + properties: { + expanded_: { + type: Boolean, + value: false, + }, + selectedSize: { + type: Number, + value: 0.250, + notify: true, + }, + selectedColor: { + type: String, + value: '#000000', + notify: true, + }, + sizes_: { + type: Array, + value: sizes, + }, + colors_: { + type: Array, + value: colors, + }, + strings: Object, + }, + + /** @type {Array} */ + expandAnimations_: null, + + /** @param {Event} e */ + sizeChanged_: function(e) { + this.selectedSize = Number(e.target.value); + }, + + /** @param {Event} e */ + colorChanged_: function(e) { + this.selectedColor = e.target.value; + }, + + /** @private */ + toggleExpanded_: function() { + this.expanded_ = !this.expanded_; + this.updateExpandedState_(); + }, + + /** @private */ + updateExpandedStateAndFinishAnimations_: function() { + this.updateExpandedState_(); + for (const animation of /** @type {!Array} */ ( + this.expandAnimations_)) { + animation.finish(); + } + }, + + /** @override */ + attached: function() { + Polymer.RenderStatus.beforeNextRender(this, () => { + this.updateExpandedStateAndFinishAnimations_(); + }); + }, + + /** + * Updates the state of the UI to reflect the current value of `expanded`. + * Starts or reverses animations and enables/disable controls. + */ + updateExpandedState_: function() { + const colors = this.$.colors; + if (!this.expandAnimations_) { + const separator = this.$.separator; + const expand = this.$.expand; + this.expandAnimations_ = [ + colors.animate({height: ['32px', '188px']}, { + easing: 'ease-in-out', + duration: 250, + fill: 'both', + }), + separator.animate({opacity: [0, 1]}, { + easing: 'ease-in-out', + duration: 250, + fill: 'both', + }), + expand.animate({transform: ['rotate(0deg)', 'rotate(180deg)']}, { + easing: 'ease-in-out', + duration: 250, + fill: 'forwards', + }), + ]; + } + for (const animation of this.expandAnimations_) { + // TODO(dstockwell): Ideally we would just set playbackRate, + // but there appears to be a web-animations bug that + // results in the animation getting stuck in the 'pending' + // state sometimes. See crbug.com/938857 + const currentTime = animation.currentTime; + animation.cancel(); + animation.playbackRate = this.expanded_ ? 1 : -1; + animation.currentTime = currentTime; + animation.play(); + } + for (const input of colors.querySelectorAll('input:nth-child(n+8)')) { + if (this.expanded_) { + input.removeAttribute('disabled'); + } else { + input.setAttribute('disabled', ''); + } + } + }, + + /** + * Used to determine equality in computed bindings. + * + * @param {*} a + * @param {*} b + */ + equal_: function(a, b) { + return a == b; + }, + + /** + * Used to lookup a string in a computed binding. + * + * @param {Object} strings + * @param {string} name + * @return {string} + */ + lookup_: function(strings, name) { + return strings ? strings[name] : ''; + }, + + /** + * Used to remove focus when clicking or tapping on a styled input + * element. This is a workaround until we can use the :focus-visible + * pseudo selector. + */ + blurOnPointerDown(e) { + const target = e.target; + setTimeout(() => target.blur(), 0); + }, +}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-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-pen-options/viewer-pen-options.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html deleted file mode 100644 index dbf18b0bfda..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js b/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js deleted file mode 100644 index 3970e0ff630..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js +++ /dev/null @@ -1,195 +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. - -const colors = [ - // row 1 - {name: 'annotationColorBlack', color: '#000000'}, - {name: 'annotationColorRed', color: '#ff5252'}, - {name: 'annotationColorYellow', color: '#ffbc00'}, - {name: 'annotationColorGreen', color: '#00c853'}, - {name: 'annotationColorCyan', color: '#00b0ff'}, - {name: 'annotationColorPurple', color: '#d500f9'}, - {name: 'annotationColorBrown', color: '#8d6e63'}, - // row 2 - {name: 'annotationColorWhite', color: '#fafafa', outline: true}, - {name: 'annotationColorCrimson', color: '#a52714'}, - {name: 'annotationColorAmber', color: '#ee8100'}, - {name: 'annotationColorAvocadoGreen', color: '#558b2f'}, - {name: 'annotationColorCobaltBlue', color: '#01579b'}, - {name: 'annotationColorDeepPurple', color: '#8e24aa'}, - {name: 'annotationColorDarkBrown', color: '#4e342e'}, - // row 3 - {name: 'annotationColorDarkGrey', color: '#90a4ae'}, - {name: 'annotationColorHotPink', color: '#ff4081'}, - {name: 'annotationColorOrange', color: '#ff6e40'}, - {name: 'annotationColorLime', color: '#aeea00'}, - {name: 'annotationColorBlue', color: '#304ffe'}, - {name: 'annotationColorViolet', color: '#7c4dff'}, - {name: 'annotationColorTeal', color: '#1de9b6'}, - // row 4 - {name: 'annotationColorLightGrey', color: '#cfd8dc'}, - {name: 'annotationColorLightPink', color: '#f8bbd0'}, - {name: 'annotationColorLightOrange', color: '#ffccbc'}, - {name: 'annotationColorLightGreen', color: '#f0f4c3'}, - {name: 'annotationColorLightBlue', color: '#9fa8da'}, - {name: 'annotationColorLavender', color: '#d1c4e9'}, - {name: 'annotationColorLightTeal', color: '#b2dfdb'}, -]; - -const sizes = [ - {name: 'annotationSize1', size: 0}, - {name: 'annotationSize2', size: 0.1429}, - {name: 'annotationSize3', size: 0.2857}, - {name: 'annotationSize4', size: 0.4286}, - {name: 'annotationSize8', size: 0.5714}, - {name: 'annotationSize12', size: 0.7143}, - {name: 'annotationSize16', size: 0.8571}, - {name: 'annotationSize20', size: 1}, -]; - -/** - * Displays a set of radio buttons to select from - * a predefined list of colors and sizes. - */ -Polymer({ - is: 'viewer-pen-options', - - properties: { - expanded_: { - type: Boolean, - value: false, - }, - selectedSize: { - type: Number, - value: 0.250, - notify: true, - }, - selectedColor: { - type: String, - value: '#000000', - notify: true, - }, - sizes_: { - type: Array, - value: sizes, - }, - colors_: { - type: Array, - value: colors, - }, - strings: Object, - }, - - /** @type {Array} */ - expandAnimations_: null, - - /** @param {Event} e */ - sizeChanged_: function(e) { - this.selectedSize = Number(e.target.value); - }, - - /** @param {Event} e */ - colorChanged_: function(e) { - this.selectedColor = e.target.value; - }, - - /** @private */ - toggleExpanded_: function() { - this.expanded_ = !this.expanded_; - this.updateExpandedState_(); - }, - - /** @private */ - updateExpandedStateAndFinishAnimations_: function() { - this.updateExpandedState_(); - for (const animation of this.expandAnimations_) { - animation.finish(); - } - }, - - /** @override */ - attached: function() { - Polymer.RenderStatus.beforeNextRender(this, () => { - this.updateExpandedStateAndFinishAnimations_(); - }); - }, - - /** - * Updates the state of the UI to reflect the current value of `expanded`. - * Starts or reverses animations and enables/disable controls. - */ - updateExpandedState_: function() { - const colors = this.$.colors; - if (!this.expandAnimations_) { - const separator = this.$.separator; - const expand = this.$.expand; - this.expandAnimations_ = [ - colors.animate({height: ['32px', '188px']}, { - easing: 'ease-in-out', - duration: 250, - fill: 'both', - }), - separator.animate({opacity: [0, 1]}, { - easing: 'ease-in-out', - duration: 250, - fill: 'both', - }), - expand.animate({transform: ['rotate(0deg)', 'rotate(180deg)']}, { - easing: 'ease-in-out', - duration: 250, - fill: 'forwards', - }), - ]; - } - for (const animation of this.expandAnimations_) { - // TODO(dstockwell): Ideally we would just set playbackRate, - // but there appears to be a web-animations bug that - // results in the animation getting stuck in the 'pending' - // state sometimes. See crbug.com/938857 - const currentTime = animation.currentTime; - animation.cancel(); - animation.playbackRate = this.expanded_ ? 1 : -1; - animation.currentTime = currentTime; - animation.play(); - } - for (const input of colors.querySelectorAll('input:nth-child(n+8)')) { - if (this.expanded_) { - input.removeAttribute('disabled'); - } else { - input.setAttribute('disabled', ''); - } - } - }, - - /** - * Used to determine equality in computed bindings. - * - * @param {*} a - * @param {*} b - */ - equal_: function(a, b) { - return a == b; - }, - - /** - * Used to lookup a string in a computed binding. - * - * @param {Object} strings - * @param {string} name - * @return {string} - */ - lookup_: function(strings, name) { - return strings ? strings[name] : ''; - }, - - /** - * Used to remove focus when clicking or tapping on a styled input - * element. This is a workaround until we can use the :focus-visible - * pseudo selector. - */ - blurOnPointerDown(e) { - const target = e.target; - setTimeout(() => target.blur(), 0); - }, -}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html new file mode 100644 index 00000000000..1ebf0df604f --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html @@ -0,0 +1,109 @@ + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js new file mode 100644 index 00000000000..191f4cfcfbf --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js @@ -0,0 +1,219 @@ +// 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() { +/** + * Size of additional padding in the inner scrollable section of the dropdown. + */ +const DROPDOWN_INNER_PADDING = 12; + +/** Size of vertical padding on the outer #dropdown element. */ +const DROPDOWN_OUTER_PADDING = 2; + +/** Minimum height of toolbar dropdowns (px). */ +const MIN_DROPDOWN_HEIGHT = 200; + +Polymer({ + is: 'viewer-toolbar-dropdown', + + properties: { + /** Icon to display when the dropdown is closed. */ + closedIcon: String, + + /** Whether the dropdown should be centered or right aligned. */ + dropdownCentered: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + + /** True if the dropdown is currently open. */ + dropdownOpen: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + + /** + * String to be displayed at the top of the dropdown and for the tooltip + * of the button. + */ + header: String, + + /** Whether to hide the header at the top of the dropdown. */ + hideHeader: { + type: Boolean, + value: false, + }, + + /** Lowest vertical point that the dropdown should occupy (px). */ + lowerBound: { + type: Number, + observer: 'lowerBoundChanged_', + }, + + /** Unique id to identify this dropdown for metrics purposes. */ + metricsId: String, + + /** Whether the dropdown must be selected before opening. */ + openAfterSelect: { + type: Boolean, + value: false, + }, + + /** Icon to display when the dropdown is open. */ + openIcon: String, + + /** Whether the dropdown is marked as selected. */ + selected: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + + /** + * Toolbar icon currently being displayed. + * @private + */ + dropdownIcon_: { + type: String, + computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)', + }, + }, + + /** + * Current animation being played, or null if there is none. + * @private {?Object} + */ + animation_: null, + + /** + * True if the max-height CSS property for the dropdown scroll container + * is valid. If false, the height will be updated the next time the + * dropdown is visible. + * @private {boolean} + */ + maxHeightValid_: false, + + /** + * @return {string} Current icon for the dropdown. + * @private + */ + computeIcon_: function(dropdownOpen, closedIcon, openIcon) { + return dropdownOpen ? openIcon : closedIcon; + }, + + /** @private */ + lowerBoundChanged_: function() { + this.maxHeightValid_ = false; + if (this.dropdownOpen) { + this.updateMaxHeight(); + } + }, + + toggleDropdown: function() { + if (!this.dropdownOpen && this.openAfterSelect && !this.selected) { + // The dropdown has `openAfterSelect` set, but is not yet selected. + return; + } + this.dropdownOpen = !this.dropdownOpen; + if (this.dropdownOpen) { + this.$.dropdown.style.display = 'block'; + if (!this.maxHeightValid_) { + this.updateMaxHeight(); + } + this.fire('dropdown-opened', this.metricsId); + } + + if (this.dropdownOpen) { + const listener = (e) => { + if (e.path.includes(this)) { + return; + } + if (this.dropdownOpen) { + this.toggleDropdown(); + this.blur(); + } + // Clean up the handler. The dropdown may already be closed. + window.removeEventListener('pointerdown', listener); + }; + window.addEventListener('pointerdown', listener); + } + + this.playAnimation_(this.dropdownOpen); + }, + + updateMaxHeight: function() { + const scrollContainer = this.$['scroll-container']; + let height = this.lowerBound - scrollContainer.getBoundingClientRect().top - + DROPDOWN_INNER_PADDING; + height = Math.max(height, MIN_DROPDOWN_HEIGHT); + scrollContainer.style.maxHeight = height + 'px'; + this.maxHeightValid_ = true; + }, + + /** + * Start an animation on the dropdown. + * @param {boolean} isEntry True to play entry animation, false to play + * exit. + * @private + */ + playAnimation_: function(isEntry) { + this.animation_ = isEntry ? this.animateEntry_() : this.animateExit_(); + this.animation_.onfinish = () => { + this.animation_ = null; + if (!this.dropdownOpen) { + this.$.dropdown.style.display = 'none'; + } + }; + }, + + /** + * @return {!Object} Animation + * @private + */ + animateEntry_: function() { + let maxHeight = + this.$.dropdown.getBoundingClientRect().height - DROPDOWN_OUTER_PADDING; + + if (maxHeight < 0) { + maxHeight = 0; + } + + this.$.dropdown.animate( + { + opacity: [0, 1], + }, + { + duration: 150, + easing: 'cubic-bezier(0, 0, 0.2, 1)', + }); + return this.$.dropdown.animate( + [ + {height: '20px', transform: 'translateY(-10px)'}, + {height: maxHeight + 'px', transform: 'translateY(0)'}, + ], + { + duration: 250, + easing: 'cubic-bezier(0, 0, 0.2, 1)', + }); + }, + + /** + * @return {!Object} Animation + * @private + */ + animateExit_: function() { + return this.$.dropdown.animate( + [ + {transform: 'translateY(0)', opacity: 1}, + {transform: 'translateY(-5px)', opacity: 0}, + ], + { + duration: 100, + easing: 'cubic-bezier(0.4, 0, 1, 1)', + }); + } +}); +})(); 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-toolbar-dropdown/viewer-toolbar-dropdown.html b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html deleted file mode 100644 index 1ebf0df604f..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js deleted file mode 100644 index 00b2b676b45..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -(function() { -/** - * Size of additional padding in the inner scrollable section of the dropdown. - */ -const DROPDOWN_INNER_PADDING = 12; - -/** Size of vertical padding on the outer #dropdown element. */ -const DROPDOWN_OUTER_PADDING = 2; - -/** Minimum height of toolbar dropdowns (px). */ -const MIN_DROPDOWN_HEIGHT = 200; - -Polymer({ - is: 'viewer-toolbar-dropdown', - - properties: { - /** Icon to display when the dropdown is closed. */ - closedIcon: String, - - /** Whether the dropdown should be centered or right aligned. */ - dropdownCentered: { - type: Boolean, - reflectToAttribute: true, - value: false, - }, - - /** True if the dropdown is currently open. */ - dropdownOpen: { - type: Boolean, - reflectToAttribute: true, - value: false, - }, - - /** - * String to be displayed at the top of the dropdown and for the tooltip - * of the button. - */ - header: String, - - /** Whether to hide the header at the top of the dropdown. */ - hideHeader: { - type: Boolean, - value: false, - }, - - /** Lowest vertical point that the dropdown should occupy (px). */ - lowerBound: { - type: Number, - observer: 'lowerBoundChanged_', - }, - - /** Unique id to identify this dropdown for metrics purposes. */ - metricsId: String, - - /** Whether the dropdown must be selected before opening. */ - openAfterSelect: { - type: Boolean, - value: false, - }, - - /** Icon to display when the dropdown is open. */ - openIcon: String, - - /** Whether the dropdown is marked as selected. */ - selected: { - type: Boolean, - reflectToAttribute: true, - value: false, - }, - - /** - * Toolbar icon currently being displayed. - * @private - */ - dropdownIcon_: { - type: String, - computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)', - }, - }, - - /** - * Current animation being played, or null if there is none. - * @private {?Object} - */ - animation_: null, - - /** - * True if the max-height CSS property for the dropdown scroll container - * is valid. If false, the height will be updated the next time the - * dropdown is visible. - * @private {boolean} - */ - maxHeightValid_: false, - - /** - * @return {string} Current icon for the dropdown. - * @private - */ - computeIcon_: function(dropdownOpen, closedIcon, openIcon) { - return dropdownOpen ? openIcon : closedIcon; - }, - - /** @private */ - lowerBoundChanged_: function() { - this.maxHeightValid_ = false; - if (this.dropdownOpen) { - this.updateMaxHeight(); - } - }, - - toggleDropdown: function() { - if (!this.dropdownOpen && this.openAfterSelect && !this.selected) { - // The dropdown has `openAfterSelect` set, but is not yet selected. - return; - } - this.dropdownOpen = !this.dropdownOpen; - if (this.dropdownOpen) { - this.$.dropdown.style.display = 'block'; - if (!this.maxHeightValid_) { - this.updateMaxHeight(); - } - this.fire('dropdown-opened', this.metricsId); - } - - if (this.dropdownOpen) { - const listener = (e) => { - if (e.path.includes(this)) { - return; - } - if (this.dropdownOpen) { - this.toggleDropdown(); - this.blur(); - } - // Clean up the handler. The dropdown may already be closed. - window.removeEventListener('pointerdown', listener); - }; - window.addEventListener('pointerdown', listener); - } - - this.playAnimation_(this.dropdownOpen); - }, - - updateMaxHeight: function() { - const scrollContainer = this.$['scroll-container']; - let height = this.lowerBound - scrollContainer.getBoundingClientRect().top - - DROPDOWN_INNER_PADDING; - height = Math.max(height, MIN_DROPDOWN_HEIGHT); - scrollContainer.style.maxHeight = height + 'px'; - this.maxHeightValid_ = true; - }, - - /** - * Start an animation on the dropdown. - * @param {boolean} isEntry True to play entry animation, false to play - * exit. - * @private - */ - playAnimation_: function(isEntry) { - this.animation_ = isEntry ? this.animateEntry_() : this.animateExit_(); - this.animation_.onfinish = () => { - this.animation_ = null; - if (!this.dropdownOpen) { - this.$.dropdown.style.display = 'none'; - } - }; - }, - - /** - * @return {!Object} Animation - * @private - */ - animateEntry_: function() { - let maxHeight = - this.$.dropdown.getBoundingClientRect().height - DROPDOWN_OUTER_PADDING; - - if (maxHeight < 0) { - maxHeight = 0; - } - - this.$.dropdown.animate( - { - opacity: [0, 1], - }, - { - duration: 150, - easing: 'cubic-bezier(0, 0, 0.2, 1)', - }); - return this.$.dropdown.animate( - [ - {height: '20px', transform: 'translateY(-10px)'}, - {height: maxHeight + 'px', transform: 'translateY(0)'}, - ], - { - duration: 250, - easing: 'cubic-bezier(0, 0, 0.2, 1)', - }); - }, - - /** - * @return {!Object} Animation - * @private - */ - animateExit_: function() { - return this.$.dropdown.animate( - [ - {transform: 'translateY(0)', opacity: 1}, - {transform: 'translateY(-5px)', opacity: 0}, - ], - { - duration: 100, - easing: 'cubic-bezier(0.4, 0, 1, 1)', - }); - } -}); - -})(); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.html new file mode 100644 index 00000000000..000d098cfb3 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.html @@ -0,0 +1,85 @@ + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.js new file mode 100644 index 00000000000..2359900d83c --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-button.js @@ -0,0 +1,134 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Polymer({ + is: 'viewer-zoom-button', + + properties: { + /** Index of the icon currently being displayed. */ + activeIndex: { + type: Number, + value: 0, + }, + + delay: { + type: Number, + observer: 'delayChanged_', + }, + + /** + * Icons to be displayed on the FAB. Multiple icons should be separated with + * spaces, and will be cycled through every time the FAB is clicked. + */ + icons: String, + + /** + * Used to show the appropriate drop shadow when buttons are focused with + * the keyboard. + */ + keyboardNavigationActive: { + type: Boolean, + reflectToAttribute: true, + }, + + newPrintPreview: { + type: Boolean, + reflectToAttribute: true, + }, + + showOnLeft: { + type: Boolean, + reflectToAttribute: true, + }, + + /** @type {?Array} */ + tooltips: Array, + + /** @private */ + closed_: { + type: Boolean, + reflectToAttribute: true, + value: false, + }, + + /** + * Array version of the list of icons. Polymer does not allow array + * properties to be set from HTML, so we must use a string property and + * perform the conversion manually. + * @private {!Array} + */ + icons_: { + type: Array, + value: [''], + computed: 'computeIconsArray_(icons)', + }, + + /** + * Icon currently being displayed on the FAB. + * @private + */ + visibleIcon_: { + type: String, + computed: 'computeVisibleIcon_(icons_, activeIndex)', + }, + + /** @private */ + visibleTooltip_: { + type: String, + computed: 'computeVisibleTooltip_(tooltips, activeIndex)', + } + }, + + /** + * @param {string} icons Icon names in a string, delimited by spaces + * @return {!Array} Array of icon name strings + * @private + */ + computeIconsArray_: function(icons) { + return icons.split(' '); + }, + + /** + * @param {!Array} icons Array of icon name strings. + * @param {number} activeIndex Index of the currently active icon. + * @return {string} Icon name for the currently visible icon. + * @private + */ + computeVisibleIcon_: function(icons, activeIndex) { + return icons[activeIndex]; + }, + + /** + * @param {?Array} tooltips Array of tooltip strings. + * @param {number} activeIndex Index of the currently active icon. + * @return {string} Tooltip for the currently visible icon. + * @private + */ + computeVisibleTooltip_: function(tooltips, activeIndex) { + return tooltips === undefined ? '' : tooltips[activeIndex]; + }, + + /** @private */ + delayChanged_: function() { + this.$.wrapper.style.transitionDelay = this.delay + 'ms'; + }, + + show: function() { + this.closed_ = false; + }, + + hide: function() { + this.closed_ = true; + }, + + /** @private */ + fireClick_: function() { + // We cannot attach an on-click to the entire viewer-zoom-button, as this + // will include clicks on the margins. Instead, proxy clicks on the FAB + // through. + this.fire('fabclick'); + + this.activeIndex = (this.activeIndex + 1) % this.icons_.length; + } +}); diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html new file mode 100644 index 00000000000..0807c5dfe64 --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html @@ -0,0 +1,73 @@ + + + + + + + + + + diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js new file mode 100644 index 00000000000..fe28ef8c96a --- /dev/null +++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js @@ -0,0 +1,198 @@ +// 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. + +/** + * @typedef {{ + * fittingType: !FittingType, + * userInitiated: boolean, + * }} + */ +let FitToChangedEvent; + +(function() { + +const FIT_TO_PAGE_BUTTON_STATE = 0; +const FIT_TO_WIDTH_BUTTON_STATE = 1; + +Polymer({ + is: 'viewer-zoom-toolbar', + + properties: { + newPrintPreview: { + type: Boolean, + reflectToAttribute: true, + }, + + /** @private */ + keyboardNavigationActive_: { + type: Boolean, + value: false, + }, + + /** @private */ + showOnLeft_: { + type: Boolean, + computed: 'computeShowOnLeft_(newPrintPreview)', + reflectToAttribute: true, + }, + }, + + listeners: { + 'focus': 'onFocus_', + 'keyup': 'onKeyUp_', + 'pointerdown': 'onPointerDown_', + }, + + /** @private {boolean} */ + isPrintPreview_: false, + + /** @private {boolean} */ + visible_: true, + + /** @param {boolean} isPrintPreview */ + setIsPrintPreview: function(isPrintPreview) { + this.isPrintPreview_ = isPrintPreview; + }, + + /** @return {boolean} */ + isPrintPreview: function() { + return this.isPrintPreview_; + }, + + /** @return {boolean} */ + isVisible: function() { + return this.visible_; + }, + + /** @private */ + onFocus_: function() { + // This can only happen when the plugin is shown within Print Preview using + // keyboard navigation. + if (!this.visible_) { + assert(this.isPrintPreview_); + this.fire('keyboard-navigation-active', true); + this.show(); + } + }, + + /** @private */ + onKeyUp_: function() { + if (this.isPrintPreview_) { + this.fire('keyboard-navigation-active', true); + } + this.keyboardNavigationActive_ = true; + }, + + /** @private */ + onPointerDown_: function() { + if (this.isPrintPreview_) { + this.fire('keyboard-navigation-active', false); + } + this.keyboardNavigationActive_ = false; + }, + + /** + * @return {boolean} Whether to show the zoom toolbar on the left side of the + * viewport. + * @private + */ + computeShowOnLeft_: function() { + return isRTL() !== this.newPrintPreview; + }, + + /** + * Change button tooltips to match any changes to localized strings. + * @param {!{tooltipFitToPage: string, + * tooltipFitToWidth: string, + * tooltipZoomIn: string, + * tooltipZoomOut: string}} strings + */ + setStrings: function(strings) { + this.$['fit-button'].tooltips = + [strings.tooltipFitToPage, strings.tooltipFitToWidth]; + this.$['zoom-in-button'].tooltips = [strings.tooltipZoomIn]; + this.$['zoom-out-button'].tooltips = [strings.tooltipZoomOut]; + }, + + /** Handle clicks of the fit-button. */ + fitToggle: function() { + this.fireFitToChangedEvent_( + this.$['fit-button'].activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? + FittingType.FIT_TO_WIDTH : + FittingType.FIT_TO_PAGE, + true); + }, + + /** Handle the keyboard shortcut equivalent of fit-button clicks. */ + fitToggleFromHotKey: function() { + this.fitToggle(); + + // Toggle the button state since there was no mouse click. + const button = this.$['fit-button']; + button.activeIndex = + (button.activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? + FIT_TO_PAGE_BUTTON_STATE : + FIT_TO_WIDTH_BUTTON_STATE); + }, + + /** + * Handle forcing zoom via scripting to a fitting type. + * @param {!FittingType} fittingType Page fitting type to force. + */ + forceFit: function(fittingType) { + this.fireFitToChangedEvent_(fittingType, false); + + // Set the button state since there was no mouse click. + const nextButtonState = + (fittingType == FittingType.FIT_TO_WIDTH ? FIT_TO_PAGE_BUTTON_STATE : + FIT_TO_WIDTH_BUTTON_STATE); + this.$['fit-button'].activeIndex = nextButtonState; + }, + + /** + * Fire a 'fit-to-changed' {CustomEvent} with the given FittingType as detail. + * @param {!FittingType} fittingType to include as payload. + * @param {boolean} userInitiated whether the event was initiated by a user + * action. + * @private + */ + fireFitToChangedEvent_: function(fittingType, userInitiated) { + this.fire( + 'fit-to-changed', + {fittingType: fittingType, userInitiated: userInitiated}); + }, + + /** + * Handle clicks of the zoom-in-button. + */ + zoomIn: function() { + this.fire('zoom-in'); + }, + + /** + * Handle clicks of the zoom-out-button. + */ + zoomOut: function() { + this.fire('zoom-out'); + }, + + show: function() { + if (!this.visible_) { + this.visible_ = true; + this.$['fit-button'].show(); + this.$['zoom-in-button'].show(); + this.$['zoom-out-button'].show(); + } + }, + + hide: function() { + if (this.visible_) { + this.visible_ = false; + this.$['fit-button'].hide(); + this.$['zoom-in-button'].hide(); + this.$['zoom-out-button'].hide(); + } + }, +}); +})(); 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/elements/viewer-zoom-toolbar/viewer-zoom-button.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html deleted file mode 100644 index 000d098cfb3..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js deleted file mode 100644 index 2359900d83c..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -Polymer({ - is: 'viewer-zoom-button', - - properties: { - /** Index of the icon currently being displayed. */ - activeIndex: { - type: Number, - value: 0, - }, - - delay: { - type: Number, - observer: 'delayChanged_', - }, - - /** - * Icons to be displayed on the FAB. Multiple icons should be separated with - * spaces, and will be cycled through every time the FAB is clicked. - */ - icons: String, - - /** - * Used to show the appropriate drop shadow when buttons are focused with - * the keyboard. - */ - keyboardNavigationActive: { - type: Boolean, - reflectToAttribute: true, - }, - - newPrintPreview: { - type: Boolean, - reflectToAttribute: true, - }, - - showOnLeft: { - type: Boolean, - reflectToAttribute: true, - }, - - /** @type {?Array} */ - tooltips: Array, - - /** @private */ - closed_: { - type: Boolean, - reflectToAttribute: true, - value: false, - }, - - /** - * Array version of the list of icons. Polymer does not allow array - * properties to be set from HTML, so we must use a string property and - * perform the conversion manually. - * @private {!Array} - */ - icons_: { - type: Array, - value: [''], - computed: 'computeIconsArray_(icons)', - }, - - /** - * Icon currently being displayed on the FAB. - * @private - */ - visibleIcon_: { - type: String, - computed: 'computeVisibleIcon_(icons_, activeIndex)', - }, - - /** @private */ - visibleTooltip_: { - type: String, - computed: 'computeVisibleTooltip_(tooltips, activeIndex)', - } - }, - - /** - * @param {string} icons Icon names in a string, delimited by spaces - * @return {!Array} Array of icon name strings - * @private - */ - computeIconsArray_: function(icons) { - return icons.split(' '); - }, - - /** - * @param {!Array} icons Array of icon name strings. - * @param {number} activeIndex Index of the currently active icon. - * @return {string} Icon name for the currently visible icon. - * @private - */ - computeVisibleIcon_: function(icons, activeIndex) { - return icons[activeIndex]; - }, - - /** - * @param {?Array} tooltips Array of tooltip strings. - * @param {number} activeIndex Index of the currently active icon. - * @return {string} Tooltip for the currently visible icon. - * @private - */ - computeVisibleTooltip_: function(tooltips, activeIndex) { - return tooltips === undefined ? '' : tooltips[activeIndex]; - }, - - /** @private */ - delayChanged_: function() { - this.$.wrapper.style.transitionDelay = this.delay + 'ms'; - }, - - show: function() { - this.closed_ = false; - }, - - hide: function() { - this.closed_ = true; - }, - - /** @private */ - fireClick_: function() { - // We cannot attach an on-click to the entire viewer-zoom-button, as this - // will include clicks on the margins. Instead, proxy clicks on the FAB - // through. - this.fire('fabclick'); - - this.activeIndex = (this.activeIndex + 1) % this.icons_.length; - } -}); 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/viewer-zoom-toolbar.html deleted file mode 100644 index e0f2bdd15ba..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js deleted file mode 100644 index fe24ab64645..00000000000 --- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -(function() { - -const FIT_TO_PAGE_BUTTON_STATE = 0; -const FIT_TO_WIDTH_BUTTON_STATE = 1; - -Polymer({ - is: 'viewer-zoom-toolbar', - - properties: { - newPrintPreview: { - type: Boolean, - reflectToAttribute: true, - }, - - /** @private */ - keyboardNavigationActive_: { - type: Boolean, - value: false, - }, - - /** @private */ - showOnLeft_: { - type: Boolean, - computed: 'computeShowOnLeft_(newPrintPreview)', - reflectToAttribute: true, - }, - }, - - listeners: { - 'focus': 'onFocus_', - 'keyup': 'onKeyUp_', - 'pointerdown': 'onPointerDown_', - }, - - /** @private {boolean} */ - isPrintPreview_: false, - - /** @private {boolean} */ - visible_: true, - - /** @param {boolean} isPrintPreview */ - setIsPrintPreview: function(isPrintPreview) { - this.isPrintPreview_ = isPrintPreview; - }, - - /** @return {boolean} */ - isPrintPreview: function() { - return this.isPrintPreview_; - }, - - /** @return {boolean} */ - isVisible: function() { - return this.visible_; - }, - - /** @private */ - onFocus_: function() { - // This can only happen when the plugin is shown within Print Preview using - // keyboard navigation. - if (!this.visible_) { - assert(this.isPrintPreview_); - this.fire('keyboard-navigation-active', true); - this.show(); - } - }, - - /** @private */ - onKeyUp_: function() { - if (this.isPrintPreview_) { - this.fire('keyboard-navigation-active', true); - } - this.keyboardNavigationActive_ = true; - }, - - /** @private */ - onPointerDown_: function() { - if (this.isPrintPreview_) { - this.fire('keyboard-navigation-active', false); - } - this.keyboardNavigationActive_ = false; - }, - - /** - * @return {boolean} Whether to show the zoom toolbar on the left side of the - * viewport. - * @private - */ - computeShowOnLeft_: function() { - return isRTL() !== this.newPrintPreview; - }, - - /** - * Change button tooltips to match any changes to localized strings. - * @param {!{tooltipFitToPage: string, - * tooltipFitToWidth: string, - * tooltipZoomIn: string, - * tooltipZoomOut: string}} strings - */ - setStrings: function(strings) { - this.$['fit-button'].tooltips = - [strings.tooltipFitToPage, strings.tooltipFitToWidth]; - this.$['zoom-in-button'].tooltips = [strings.tooltipZoomIn]; - this.$['zoom-out-button'].tooltips = [strings.tooltipZoomOut]; - }, - - /** Handle clicks of the fit-button. */ - fitToggle: function() { - this.fireFitToChangedEvent_( - this.$['fit-button'].activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? - FittingType.FIT_TO_WIDTH : - FittingType.FIT_TO_PAGE, - true); - }, - - /** Handle the keyboard shortcut equivalent of fit-button clicks. */ - fitToggleFromHotKey: function() { - this.fitToggle(); - - // Toggle the button state since there was no mouse click. - const button = this.$['fit-button']; - button.activeIndex = - (button.activeIndex == FIT_TO_WIDTH_BUTTON_STATE ? - FIT_TO_PAGE_BUTTON_STATE : - FIT_TO_WIDTH_BUTTON_STATE); - }, - - /** - * Handle forcing zoom via scripting to a fitting type. - * @param {!FittingType} fittingType Page fitting type to force. - */ - forceFit: function(fittingType) { - this.fireFitToChangedEvent_(fittingType, false); - - // Set the button state since there was no mouse click. - const nextButtonState = - (fittingType == FittingType.FIT_TO_WIDTH ? FIT_TO_PAGE_BUTTON_STATE : - FIT_TO_WIDTH_BUTTON_STATE); - this.$['fit-button'].activeIndex = nextButtonState; - }, - - /** - * Fire a 'fit-to-changed' {CustomEvent} with the given FittingType as detail. - * @param {!FittingType} fittingType to include as payload. - * @param {boolean} userInitiated whether the event was initiated by a user - * action. - * @private - */ - fireFitToChangedEvent_: function(fittingType, userInitiated) { - this.fire( - 'fit-to-changed', - {fittingType: fittingType, userInitiated: userInitiated}); - }, - - /** - * Handle clicks of the zoom-in-button. - */ - zoomIn: function() { - this.fire('zoom-in'); - }, - - /** - * Handle clicks of the zoom-out-button. - */ - zoomOut: function() { - this.fire('zoom-out'); - }, - - show: function() { - if (!this.visible_) { - this.visible_ = true; - this.$['fit-button'].show(); - this.$['zoom-in-button'].show(); - this.$['zoom-out-button'].show(); - } - }, - - hide: function() { - if (this.visible_) { - this.visible_ = false; - this.$['fit-button'].hide(); - this.$['zoom-in-button'].hide(); - this.$['zoom-out-button'].hide(); - } - }, -}); -})(); 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 @@ - - - - - - + + + + + + - - + + @@ -58,6 +58,7 @@ + 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 @@ -2,15 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** - * @typedef {{ - * tool: string, - * size: number, - * color: string, - * }} - */ -let AnnotationTool; - /** * @typedef {{ * canUndo: 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} */ 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} 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, + * 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}} */ (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} */ + 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} */ - 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} 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} 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} 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} 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} - * @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} */ - 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, * }} */ 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; @@ -278,6 +285,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 @@ -4,76 +4,62 @@ '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; -- cgit v1.2.3