Merge pull request #11380 from Snuffleupagus/PDFHistory-reset

Add a `reset` method to the `PDFHistory` implementation
This commit is contained in:
Tim van der Meij 2019-12-15 16:45:53 +01:00 committed by GitHub
commit 7ceb394c43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 63 deletions

View File

@ -130,6 +130,10 @@ var PDFViewerApplication = {
this.pdfViewer.setDocument(null); this.pdfViewer.setDocument(null);
this.pdfLinkService.setDocument(null, null); this.pdfLinkService.setDocument(null, null);
if (this.pdfHistory) {
this.pdfHistory.reset();
}
} }
return promise; return promise;

View File

@ -593,6 +593,9 @@ let PDFViewerApplication = {
this.pdfOutlineViewer.reset(); this.pdfOutlineViewer.reset();
this.pdfAttachmentViewer.reset(); this.pdfAttachmentViewer.reset();
if (this.pdfHistory) {
this.pdfHistory.reset();
}
if (this.findBar) { if (this.findBar) {
this.findBar.reset(); this.findBar.reset();
} }

View File

@ -101,6 +101,8 @@ class IPDFHistory {
*/ */
initialize({ fingerprint, resetHistory = false, updateUrl = false, }) {} initialize({ fingerprint, resetHistory = false, updateUrl = false, }) {}
reset() {}
/** /**
* @param {Object} params * @param {Object} params
*/ */

View File

@ -50,17 +50,6 @@ function getCurrentHash() {
return document.location.hash; return document.location.hash;
} }
function parseCurrentHash(linkService) {
let hash = unescape(getCurrentHash()).substring(1);
let params = parseQueryString(hash);
let page = params.page | 0;
if (!(Number.isInteger(page) && page > 0 && page <= linkService.pagesCount)) {
page = null;
}
return { hash, page, rotation: linkService.rotation, };
}
class PDFHistory { class PDFHistory {
/** /**
* @param {PDFHistoryOptions} options * @param {PDFHistoryOptions} options
@ -69,21 +58,25 @@ class PDFHistory {
this.linkService = linkService; this.linkService = linkService;
this.eventBus = eventBus || getGlobalEventBus(); this.eventBus = eventBus || getGlobalEventBus();
this.initialized = false; this._initialized = false;
this.initialBookmark = null; this._fingerprint = '';
this.initialRotation = null; this.reset();
this._boundEvents = Object.create(null); this._boundEvents = null;
this._isViewerInPresentationMode = false; this._isViewerInPresentationMode = false;
this._isPagesLoaded = false;
// Ensure that we don't miss either a 'presentationmodechanged' or a // Ensure that we don't miss either a 'presentationmodechanged' or a
// 'pagesloaded' event, by registering the listeners immediately. // 'pagesinit' event, by registering the listeners immediately.
this.eventBus.on('presentationmodechanged', (evt) => { this.eventBus.on('presentationmodechanged', (evt) => {
this._isViewerInPresentationMode = evt.active || evt.switchInProgress; this._isViewerInPresentationMode = evt.active || evt.switchInProgress;
}); });
this.eventBus.on('pagesloaded', (evt) => { this.eventBus.on('pagesinit', () => {
this._isPagesLoaded = false;
const onPagesLoaded = (evt) => {
this.eventBus.off('pagesloaded', onPagesLoaded);
this._isPagesLoaded = !!evt.pagesCount; this._isPagesLoaded = !!evt.pagesCount;
};
this.eventBus.on('pagesloaded', onPagesLoaded);
}); });
} }
@ -98,18 +91,18 @@ class PDFHistory {
'PDFHistory.initialize: The "fingerprint" must be a non-empty string.'); 'PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
return; return;
} }
let reInitialized = this.initialized && this.fingerprint !== fingerprint; // Ensure that any old state is always reset upon initialization.
this.fingerprint = fingerprint; if (this._initialized) {
this.reset();
}
const reInitialized = (this._fingerprint !== '' &&
this._fingerprint !== fingerprint);
this._fingerprint = fingerprint;
this._updateUrl = (updateUrl === true); this._updateUrl = (updateUrl === true);
if (!this.initialized) { this._initialized = true;
this._bindEvents(); this._bindEvents();
} const state = window.history.state;
let state = window.history.state;
this.initialized = true;
this.initialBookmark = null;
this.initialRotation = null;
this._popStateInProgress = false; this._popStateInProgress = false;
this._blockHashChange = 0; this._blockHashChange = 0;
@ -121,7 +114,7 @@ class PDFHistory {
this._position = null; this._position = null;
if (!this._isValidState(state, /* checkReload = */ true) || resetHistory) { if (!this._isValidState(state, /* checkReload = */ true) || resetHistory) {
let { hash, page, rotation, } = parseCurrentHash(this.linkService); const { hash, page, rotation, } = this._parseCurrentHash();
if (!hash || reInitialized || resetHistory) { if (!hash || reInitialized || resetHistory) {
// Ensure that the browser history is reset on PDF document load. // Ensure that the browser history is reset on PDF document load.
@ -145,29 +138,48 @@ class PDFHistory {
} }
if (destination.rotation !== undefined) { if (destination.rotation !== undefined) {
this.initialRotation = destination.rotation; this._initialRotation = destination.rotation;
} }
if (destination.dest) { if (destination.dest) {
this.initialBookmark = JSON.stringify(destination.dest); this._initialBookmark = JSON.stringify(destination.dest);
// If the history is updated, e.g. through the user changing the hash, // If the history is updated, e.g. through the user changing the hash,
// before the initial destination has become visible, then we do *not* // before the initial destination has become visible, then we do *not*
// want to potentially add `this._position` to the browser history. // want to potentially add `this._position` to the browser history.
this._destination.page = null; this._destination.page = null;
} else if (destination.hash) { } else if (destination.hash) {
this.initialBookmark = destination.hash; this._initialBookmark = destination.hash;
} else if (destination.page) { } else if (destination.page) {
// Fallback case; shouldn't be necessary, but better safe than sorry. // Fallback case; shouldn't be necessary, but better safe than sorry.
this.initialBookmark = `page=${destination.page}`; this._initialBookmark = `page=${destination.page}`;
} }
} }
/**
* Reset the current `PDFHistory` instance, and consequently prevent any
* further updates and/or navigation of the browser history.
*/
reset() {
if (this._initialized) {
this._pageHide(); // Simulate a 'pagehide' event when resetting.
this._initialized = false;
this._unbindEvents();
}
if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout);
this._updateViewareaTimeout = null;
}
this._initialBookmark = null;
this._initialRotation = null;
}
/** /**
* Push an internal destination to the browser history. * Push an internal destination to the browser history.
* @param {PushParameters} * @param {PushParameters}
*/ */
push({ namedDest = null, explicitDest, pageNumber, }) { push({ namedDest = null, explicitDest, pageNumber, }) {
if (!this.initialized) { if (!this._initialized) {
return; return;
} }
if (namedDest && typeof namedDest !== 'string') { if (namedDest && typeof namedDest !== 'string') {
@ -237,7 +249,7 @@ class PDFHistory {
* Push the current position to the browser history. * Push the current position to the browser history.
*/ */
pushCurrentPosition() { pushCurrentPosition() {
if (!this.initialized || this._popStateInProgress) { if (!this._initialized || this._popStateInProgress) {
return; return;
} }
this._tryPushCurrentPosition(); this._tryPushCurrentPosition();
@ -248,7 +260,7 @@ class PDFHistory {
* NOTE: Avoids navigating away from the document, useful for "named actions". * NOTE: Avoids navigating away from the document, useful for "named actions".
*/ */
back() { back() {
if (!this.initialized || this._popStateInProgress) { if (!this._initialized || this._popStateInProgress) {
return; return;
} }
let state = window.history.state; let state = window.history.state;
@ -262,7 +274,7 @@ class PDFHistory {
* NOTE: Avoids navigating away from the document, useful for "named actions". * NOTE: Avoids navigating away from the document, useful for "named actions".
*/ */
forward() { forward() {
if (!this.initialized || this._popStateInProgress) { if (!this._initialized || this._popStateInProgress) {
return; return;
} }
let state = window.history.state; let state = window.history.state;
@ -276,17 +288,25 @@ class PDFHistory {
* browser history, useful e.g. for skipping the next 'hashchange' event. * browser history, useful e.g. for skipping the next 'hashchange' event.
*/ */
get popStateInProgress() { get popStateInProgress() {
return this.initialized && return this._initialized &&
(this._popStateInProgress || this._blockHashChange > 0); (this._popStateInProgress || this._blockHashChange > 0);
} }
get initialBookmark() {
return this._initialized ? this._initialBookmark : null;
}
get initialRotation() {
return this._initialized ? this._initialRotation : null;
}
/** /**
* @private * @private
*/ */
_pushOrReplaceState(destination, forceReplace = false) { _pushOrReplaceState(destination, forceReplace = false) {
let shouldReplace = forceReplace || !this._destination; let shouldReplace = forceReplace || !this._destination;
let newState = { let newState = {
fingerprint: this.fingerprint, fingerprint: this._fingerprint,
uid: shouldReplace ? this._uid : (this._uid + 1), uid: shouldReplace ? this._uid : (this._uid + 1),
destination, destination,
}; };
@ -378,12 +398,12 @@ class PDFHistory {
if (!state) { if (!state) {
return false; return false;
} }
if (state.fingerprint !== this.fingerprint) { if (state.fingerprint !== this._fingerprint) {
if (checkReload) { if (checkReload) {
// Potentially accept the history entry, even if the fingerprints don't // Potentially accept the history entry, even if the fingerprints don't
// match, when the viewer was reloaded (see issue 6847). // match, when the viewer was reloaded (see issue 6847).
if (typeof state.fingerprint !== 'string' || if (typeof state.fingerprint !== 'string' ||
state.fingerprint.length !== this.fingerprint.length) { state.fingerprint.length !== this._fingerprint.length) {
return false; return false;
} }
const [perfEntry] = performance.getEntriesByType('navigation'); const [perfEntry] = performance.getEntriesByType('navigation');
@ -427,6 +447,20 @@ class PDFHistory {
this._numPositionUpdates = 0; this._numPositionUpdates = 0;
} }
/**
* @private
*/
_parseCurrentHash() {
const hash = unescape(getCurrentHash()).substring(1);
let page = parseQueryString(hash).page | 0;
if (!(Number.isInteger(page) &&
page > 0 && page <= this.linkService.pagesCount)) {
page = null;
}
return { hash, page, rotation: this.linkService.rotation, };
}
/** /**
* @private * @private
*/ */
@ -493,13 +527,13 @@ class PDFHistory {
let newHash = getCurrentHash(), hashChanged = this._currentHash !== newHash; let newHash = getCurrentHash(), hashChanged = this._currentHash !== newHash;
this._currentHash = newHash; this._currentHash = newHash;
if (!state || if ((typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME') &&
(typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME') && state && state.chromecomState && !this._isValidState(state)) ||
state.chromecomState && !this._isValidState(state))) { !state) {
// This case corresponds to the user changing the hash of the document. // This case corresponds to the user changing the hash of the document.
this._uid++; this._uid++;
let { hash, page, rotation, } = parseCurrentHash(this.linkService); const { hash, page, rotation, } = this._parseCurrentHash();
this._pushOrReplaceState({ hash, page, rotation, }, this._pushOrReplaceState({ hash, page, rotation, },
/* forceReplace = */ true); /* forceReplace = */ true);
return; return;
@ -564,12 +598,7 @@ class PDFHistory {
/** /**
* @private * @private
*/ */
_bindEvents() { _pageHide() {
let { _boundEvents, eventBus, } = this;
_boundEvents.updateViewarea = this._updateViewarea.bind(this);
_boundEvents.popState = this._popState.bind(this);
_boundEvents.pageHide = (evt) => {
// Attempt to push the `this._position` into the browser history when // Attempt to push the `this._position` into the browser history when
// navigating away from the document. This is *only* done if the history // navigating away from the document. This is *only* done if the history
// is empty/temporary, since otherwise an existing browser history entry // is empty/temporary, since otherwise an existing browser history entry
@ -578,11 +607,38 @@ class PDFHistory {
if (!this._destination || this._destination.temporary) { if (!this._destination || this._destination.temporary) {
this._tryPushCurrentPosition(); this._tryPushCurrentPosition();
} }
}
/**
* @private
*/
_bindEvents() {
if (this._boundEvents) {
return; // The event listeners were already added.
}
this._boundEvents = {
updateViewarea: this._updateViewarea.bind(this),
popState: this._popState.bind(this),
pageHide: this._pageHide.bind(this),
}; };
eventBus.on('updateviewarea', _boundEvents.updateViewarea); this.eventBus.on('updateviewarea', this._boundEvents.updateViewarea);
window.addEventListener('popstate', _boundEvents.popState); window.addEventListener('popstate', this._boundEvents.popState);
window.addEventListener('pagehide', _boundEvents.pageHide); window.addEventListener('pagehide', this._boundEvents.pageHide);
}
/**
* @private
*/
_unbindEvents() {
if (!this._boundEvents) {
return; // The event listeners were already removed.
}
this.eventBus.off('updateviewarea', this._boundEvents.updateViewarea);
window.removeEventListener('popstate', this._boundEvents.popState);
window.removeEventListener('pagehide', this._boundEvents.pageHide);
this._boundEvents = null;
} }
} }