| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import {ZoomBehavior} from './browser_api.js'; |
| |
| |
| const MIN_ZOOM_DELTA = 0.01; |
| |
| /** @return Whether two numbers are approximately equal. */ |
| function floatingPointEquals(a: number, b: number): boolean { |
| // If the zoom level is close enough to the current zoom level, don't |
| // change it. This avoids us getting into an infinite loop of zoom changes |
| // due to floating point error. |
| return Math.abs(a - b) <= MIN_ZOOM_DELTA; |
| } |
| |
| // Abstract parent of classes that manage updating the browser with zoom changes |
| // and/or updating the viewer's zoom when the browser zoom changes. |
| export abstract class ZoomManager { |
| protected browserZoom: number; |
| protected getViewportZoom: () => number; |
| private eventTarget_ = new EventTarget(); |
| |
| /** |
| * @param getViewportZoomCallback Callback to get the viewport's current zoom |
| * level. |
| * @param initialZoom The initial browser zoom level. |
| */ |
| constructor(getViewportZoomCallback: () => number, initialZoom: number) { |
| this.browserZoom = initialZoom; |
| this.getViewportZoom = getViewportZoomCallback; |
| } |
| |
| getEventTarget(): EventTarget { |
| return this.eventTarget_; |
| } |
| |
| /** |
| * Creates the appropriate kind of zoom manager given the zoom behavior. |
| * @param zoomBehavior How to manage zoom. |
| * @param getViewportZoom A function that gets the current viewport zoom. |
| * @param setBrowserZoomFunction A function that sets the browser zoom to the |
| * provided value. |
| * @param initialZoom The initial browser zoom level. |
| */ |
| static create( |
| zoomBehavior: ZoomBehavior, getViewportZoom: () => number, |
| setBrowserZoomFunction: (zoom: number) => Promise<void>, |
| initialZoom: number): ZoomManager { |
| switch (zoomBehavior) { |
| case ZoomBehavior.MANAGE: |
| return new ActiveZoomManager( |
| getViewportZoom, setBrowserZoomFunction, initialZoom); |
| case ZoomBehavior.PROPAGATE_PARENT: |
| return new EmbeddedZoomManager(getViewportZoom, initialZoom); |
| default: |
| return new InactiveZoomManager(getViewportZoom, initialZoom); |
| } |
| } |
| |
| /** |
| * Invoked when a browser-initiated zoom-level change occurs. |
| * @param newZoom the zoom level to zoom to. |
| */ |
| abstract onBrowserZoomChange(newZoom: number): void; |
| |
| /** Invoked when an extension-initiated zoom-level change occurs. */ |
| abstract onPdfZoomChange(): void; |
| |
| /** |
| * Combines the internal pdf zoom and the browser zoom to |
| * produce the total zoom level for the viewer. |
| * @param internalZoom the zoom level internal to the viewer. |
| * @return the total zoom level. |
| */ |
| applyBrowserZoom(internalZoom: number): number { |
| return this.browserZoom * internalZoom; |
| } |
| |
| /** |
| * Given a zoom level, return the internal zoom level needed to |
| * produce that zoom level. |
| * @param totalZoom the total zoom level. |
| * @return the zoom level internal to the viewer. |
| */ |
| internalZoomComponent(totalZoom: number): number { |
| return totalZoom / this.browserZoom; |
| } |
| } |
| |
| // Has no control over the browser's zoom and does not respond to browser zoom |
| // changes. |
| export class InactiveZoomManager extends ZoomManager { |
| override onBrowserZoomChange(_newZoom: number) {} |
| override onPdfZoomChange() {} |
| } |
| |
| // ActiveZoomManager controls the browser's zoom. |
| class ActiveZoomManager extends ZoomManager { |
| private setBrowserZoomFunction_: (zoom: number) => Promise<void>; |
| private changingBrowserZoom_: Promise<void>|null = null; |
| |
| /** |
| * Constructs a ActiveZoomManager. |
| * @param getViewportZoom A function that gets the current viewport zoom level |
| * @param setBrowserZoomFunction A function that sets the browser zoom to the |
| * provided value. |
| * @param initialZoom The initial browser zoom level. |
| */ |
| constructor( |
| getViewportZoom: () => number, |
| setBrowserZoomFunction: (zoom: number) => Promise<void>, |
| initialZoom: number) { |
| super(getViewportZoom, initialZoom); |
| this.setBrowserZoomFunction_ = setBrowserZoomFunction; |
| } |
| |
| onBrowserZoomChange(newZoom: number) { |
| // If we are changing the browser zoom level, ignore any browser zoom level |
| // change events. Either, the change occurred before our update and will be |
| // overwritten, or the change being reported is the change we are making, |
| // which we have already handled. |
| if (this.changingBrowserZoom_) { |
| return; |
| } |
| |
| if (floatingPointEquals(this.browserZoom, newZoom)) { |
| return; |
| } |
| |
| this.browserZoom = newZoom; |
| this.getEventTarget().dispatchEvent( |
| new CustomEvent('set-zoom', {detail: newZoom})); |
| } |
| |
| override onPdfZoomChange() { |
| // If we are already changing the browser zoom level in response to a |
| // previous extension-initiated zoom-level change, ignore this zoom change. |
| // Once the browser zoom level is changed, we check whether the extension's |
| // zoom level matches the most recently sent zoom level. |
| if (this.changingBrowserZoom_) { |
| return; |
| } |
| |
| const viewportZoom = this.getViewportZoom(); |
| if (floatingPointEquals(this.browserZoom, viewportZoom)) { |
| return; |
| } |
| |
| this.changingBrowserZoom_ = |
| this.setBrowserZoomFunction_(viewportZoom).then(() => { |
| this.browserZoom = viewportZoom; |
| this.changingBrowserZoom_ = null; |
| |
| // The extension's zoom level may have changed while the browser zoom |
| // change was in progress. We call back into onPdfZoomChange to ensure |
| // the browser zoom is up to date. |
| this.onPdfZoomChange(); |
| }); |
| } |
| |
| /** |
| * Combines the internal pdf zoom and the browser zoom to |
| * produce the total zoom level for the viewer. |
| * @param internalZoom the zoom level internal to the viewer. |
| * @return the total zoom level. |
| */ |
| override applyBrowserZoom(internalZoom: number): number { |
| // The internal zoom and browser zoom are changed together, so the |
| // browser zoom is already applied. |
| return internalZoom; |
| } |
| |
| /** |
| * Given a zoom level, return the internal zoom level needed to |
| * produce that zoom level. |
| * @param totalZoom the total zoom level. |
| * @return the zoom level internal to the viewer. |
| */ |
| override internalZoomComponent(totalZoom: number): number { |
| // The internal zoom and browser zoom are changed together, so the |
| // internal zoom is the total zoom. |
| return totalZoom; |
| } |
| } |
| |
| // Responds to changes in the browser zoom, but does not control the browser |
| // zoom. |
| class EmbeddedZoomManager extends ZoomManager { |
| /** |
| * Invoked when a browser-initiated zoom-level change occurs. |
| * @param newZoom the new browser zoom level. |
| */ |
| override onBrowserZoomChange(newZoom: number) { |
| const oldZoom = this.browserZoom; |
| this.browserZoom = newZoom; |
| this.getEventTarget().dispatchEvent( |
| new CustomEvent('update-zoom-from-browser', {detail: oldZoom})); |
| } |
| |
| override onPdfZoomChange() {} |
| } |