author | Luke Chang <lchang@mozilla.com> |
Mon, 09 Jan 2017 14:43:21 +0800 | |
changeset 329906 | ae0f6512ab35dea681bc48be51aa15a845621dcc |
parent 329905 | 27346172ee35332e28011fc4143de2bce10eaef8 |
child 329907 | 1d88fc311d4b546cf587698a836ff7880003b43f |
push id | 36173 |
push user | ryanvm@gmail.com |
push date | Wed, 18 Jan 2017 15:11:11 +0000 |
treeherder | autoland@ae0f6512ab35 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | evelyn |
bugs | 1299405 |
milestone | 53.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/extensions/mortar/host/pdf/chrome/js/toolbar.js +++ b/browser/extensions/mortar/host/pdf/chrome/js/toolbar.js @@ -199,16 +199,20 @@ class Toolbar { this._viewport.save(); break; case 'pageRotateCw': this._viewport.rotateClockwise(); break; case 'pageRotateCcw': this._viewport.rotateCounterClockwise(); break; + case 'presentationMode': + case 'secondaryPresentationMode': + this._viewport.fullscreen = true; + break; case 'secondaryToolbarToggle': this._secondaryToolbar.toggle(); break; } } _pageNumberChanged() { let newPage = parseFloat(this._elements.pageNumber.value);
--- a/browser/extensions/mortar/host/pdf/chrome/js/viewport.js +++ b/browser/extensions/mortar/host/pdf/chrome/js/viewport.js @@ -1,25 +1,24 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 'use strict'; class Viewport { constructor() { + this._viewerContainer = document.getElementById('viewerContainer'); + this._fullscreenWrapper = document.getElementById('fullscreenWrapper'); this._canvasContainer = document.getElementById('canvasContainer'); this._viewportController = document.getElementById('viewportController'); this._sizer = document.getElementById('sizer'); + this._fullscreenStatus = 'none'; this._scrollbarWidth = this._getScrollbarWidth(); - this._hasVisibleScrollbars = { - horizontal: false, - vertical: false - }; this._page = 0; this._zoom = 1; this._fitting = 'auto'; // Caches the next position set during a series of actions and will be set // to scrollbar's position until calling "_refresh". this._nextPosition = null; @@ -92,16 +91,42 @@ class Viewport { return; } newPage = Math.max(0, Math.min(pageCount - 1, newPage)); this._setPage(newPage); this._refresh(); } + get fullscreen() { + return this._fullscreenStatus != 'none'; + } + + set fullscreen(enable) { + if (this._fullscreenStatus == 'changing' || + this._fullscreenStatus == (enable ? 'fullscreen' : 'none')) { + return; + } + + // The next step after sending "setFullscreen" will happen in the function + // "_handleFullscreenChange" triggered by "fullscreenChange" event. The + // "_fullscreenStatus" will also be reset there. Note that the viewport + // stops refreshing while in the "changing" status to avoid flickers. + // + // XXX: Since we rely on "fullscreenChange" event to reset the status, the + // viewport might freeze if, for some reason, the event isn't sent back + // and we get stuck in the "changing" status. Not sure if it's the case + // we need to worry about though. + this._fullscreenStatus = 'changing'; + this._doAction({ + type: 'setFullscreen', + fullscreen: enable + }); + } + _getScrollbarWidth() { var div = document.createElement('div'); div.style.visibility = 'hidden'; div.style.overflow = 'scroll'; div.style.width = '50px'; div.style.height = '50px'; div.style.position = 'absolute'; document.body.appendChild(div); @@ -144,29 +169,35 @@ class Viewport { this._setZoom(this._computeFittingZoom()); this._setPage(this._page); if (typeof this.onDimensionChanged === 'function') { this.onDimensionChanged(); } this._refresh(); } - _computeFittingZoom() { + _computeFittingZoom(pageIndex) { let newZoom = this._zoom; let fitting = this._fitting; - if (fitting == 'none') { + if (pageIndex === undefined) { + pageIndex = this._page; + } + + if (fitting == 'none' || pageIndex < 0 || pageIndex >= this.pageCount) { return newZoom; } let FITTING_PADDING = 40; let MAX_AUTO_ZOOM = 1.25; - let page = this._pageDimensions[this._page]; - let viewportRect = this.getBoundingClientRect(); + let page = this._pageDimensions[pageIndex]; + let viewportRect = this.fullscreen ? + this._viewerContainer.getBoundingClientRect() : + this.getBoundingClientRect(); let pageWidthZoom = (viewportRect.width - FITTING_PADDING) / page.width; let pageHeightZoom = viewportRect.height / page.height; switch (fitting) { case 'auto': let isLandscape = (page.width > page.height); // For pages in landscape mode, fit the page height to the viewer @@ -284,29 +315,40 @@ class Viewport { this.onZoomChanged(this._zoom); } } _setPage(newPage) { if (newPage < 0 || newPage >= this.pageCount) { return; } + + if (this.fullscreen) { + let pageDimension = this._pageDimensions[newPage]; + let newZoom = this._computeFittingZoom(newPage); + + this._fullscreenWrapper.style.width = + (pageDimension.width * newZoom) + 'px'; + this._fullscreenWrapper.style.height = + (pageDimension.height * newZoom) + 'px'; + + if (newZoom != this._zoom) { + this._setZoom(newZoom); + } + this._notifyRuntimeOfResized(); + } + this._setPosition( this._pageDimensions[newPage].x * this._zoom, this._pageDimensions[newPage].y * this._zoom ); } _updateCanvasSize() { let hasScrollbars = this._documentHasVisibleScrollbars(this._zoom); - if (hasScrollbars.horizontal == this._hasVisibleScrollbars.horizontal && - hasScrollbars.vertical == this._hasVisibleScrollbars.vertical) { - return; - } - this._hasVisibleScrollbars = hasScrollbars; this._canvasContainer.style.bottom = hasScrollbars.horizontal ? this._scrollbarWidth + 'px' : 0; this._canvasContainer.style.right = hasScrollbars.vertical ? this._scrollbarWidth + 'px' : 0; this._notifyRuntimeOfResized(); } _contentSizeChanged() { @@ -344,16 +386,49 @@ class Viewport { if (typeof listener === 'function') { listener(evt); } else if (typeof listener.handleEvent === 'function') { listener.handleEvent(evt); } }); } + _handleFullscreenChange(fullscreen) { + // Set status to "changing" again in case it isn't triggered by setter. + this._fullscreenStatus = 'changing'; + this._viewerContainer.classList.toggle('pdfPresentationMode', fullscreen); + + // XXX: DOM elements' size changing hasn't taken place when fullscreenChange + // event is triggered in our setup, so "setTimeout" is necessary to get + // the exact size. The 100ms delay is set based on try-and-error. We + // might need to find a proper way to know when exactly the resizing is + // done. + setTimeout(() => { + let currentPage = this._page; + + if (fullscreen) { + this._previousZoom = this._zoom; + this._previousFitting = this._fitting; + this._fitting = 'page-fit'; + // No need to call "_setZoom" here because we will deal with zooming + // case in the "_setPage" below. + } else { + this._zoom = this._previousZoom; + this._fitting = this._previousFitting; + this._setZoom(this._computeFittingZoom()); + } + + this._fullscreenStatus = fullscreen ? 'fullscreen' : 'none'; + + // Reset position to the beginning of the current page. + this._setPage(currentPage); + this._refresh(); + }, 100); + } + _getEventTarget(type) { switch(type) { case 'keydown': case 'keyup': case 'keypress': return window; } return this._viewportController; @@ -361,16 +436,20 @@ class Viewport { _doAction(message) { if (this._actionHandler) { this._actionHandler(message); } } _refresh() { + if (this._fullscreenStatus == 'changing') { + return; + } + if (this._nextPosition) { this._viewportController.scrollTo( this._nextPosition.x, this._nextPosition.y); this._nextPosition = null; } this._runtimePosition = this.getScrollOffset(); this._doAction({ @@ -387,31 +466,38 @@ class Viewport { this.onPageChanged(newPage); } } } handleEvent(evt) { switch(evt.type) { case 'resize': - this._resize(); - this._notifyRuntimeOfResized(); - this._refresh(); + this.invokeResize(); break; case 'scroll': this._nextPosition = null; let position = this.getScrollOffset(); if (this._runtimePosition.x != position.x || this._runtimePosition.y != position.y) { this._refresh(); } break; } } + invokeResize() { + if (this._fullscreenStatus == 'changing') { + return; + } + this._resize(); + this._notifyRuntimeOfResized(); + this._refresh(); + } + rotateClockwise() { this._doAction({ type: 'rotateClockwise' }); } rotateCounterClockwise() { this._doAction({ @@ -497,11 +583,14 @@ class Viewport { notify(message) { switch (message.type) { case 'loadProgress': this._updateProgress(message.progress); break; case 'documentDimensions': this._setDocumentDimensions(message); break; + case 'fullscreenChange': + this._handleFullscreenChange(message.fullscreen); + break; } } }
--- a/browser/extensions/mortar/host/pdf/chrome/style/viewer.css +++ b/browser/extensions/mortar/host/pdf/chrome/style/viewer.css @@ -48,40 +48,16 @@ select { .hidden { display: none !important; } [hidden] { display: none !important; } -#viewerContainer.pdfPresentationMode:fullscreen { - top: 0px; - border-top: 2px solid transparent; - background-color: #000; - width: 100%; - height: 100%; - overflow: hidden; - cursor: none; - -moz-user-select: none; -} - -.pdfPresentationMode:fullscreen a:not(.internalLink) { - display: none; -} - -.pdfPresentationMode:fullscreen .textLayer > div { - cursor: none; -} - -.pdfPresentationMode.pdfPresentationModeControls > *, -.pdfPresentationMode.pdfPresentationModeControls .textLayer > div { - cursor: default; -} - /* outer/inner center provides horizontal center */ .outerCenter { pointer-events: none; position: relative; } html[dir='ltr'] .outerCenter { float: right; right: 50%; @@ -173,33 +149,55 @@ html[dir='ltr'] #sidebarContent { html[dir='rtl'] #sidebarContent { right: 0; box-shadow: inset 1px 0 0 hsla(0,0%,0%,.25); } #viewerContainer { overflow: hidden; position: absolute; - top: var(--toolbar-height); + top: calc(var(--toolbar-height) + 2px); right: 0; bottom: 0; left: 0; outline: none; } html[dir='ltr'] #viewerContainer { box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05); } html[dir='rtl'] #viewerContainer { box-shadow: inset -1px 0 0 hsla(0,0%,100%,.05); } +#viewerContainer.pdfPresentationMode { + position: fixed; + top: 0; + left: 0; + background-color: #000; + width: 100%; + height: 100%; + overflow: hidden; + cursor: none; + -moz-user-select: none; + z-index: 99999; + + display: flex; + align-items: center; + justify-content: center; +} + +.pdfPresentationMode #fullscreenWrapper { + position: relative; + overflow: hidden; +} + #canvasContainer, #viewportController { position: absolute; left: 0; - top: 2px; + top: 0; right: 0; bottom: 0; outline: none; overflow: hidden; } #canvasContainer canvas { display: block; @@ -207,16 +205,20 @@ html[dir='rtl'] #viewerContainer { left: 0; top: 0; } #viewportController { overflow: auto; } +.pdfPresentationMode #viewportController { + overflow: hidden; +} + #sizer { margin: 0 auto; } .toolbar { position: relative; left: 0; right: 0;
--- a/browser/extensions/mortar/host/pdf/chrome/viewer.html +++ b/browser/extensions/mortar/host/pdf/chrome/viewer.html @@ -209,19 +209,21 @@ <div class="glimmer"> </div> </div> </div> </div> </div> <div id="viewerContainer"> - <div id="canvasContainer"></div> - <div id="viewportController" tabindex="0"> - <div id="sizer"></div> + <div id="fullscreenWrapper"> + <div id="canvasContainer"></div> + <div id="viewportController" tabindex="0"> + <div id="sizer"></div> + </div> </div> </div> <div id="errorWrapper" hidden='true'> <div id="errorMessageLeft"> <span id="errorMessage"></span> <button id="errorShowMore" data-l10n-id="error_more_info"> More Information