Skip to content

Commit e389ed6

Browse files
Merge pull request #12493 from Snuffleupagus/PDFHistory.pushPage
Support adding pages, in addition to regular destinations, to the browser history and use it with thumbnails (issue 12440)
2 parents 4b4ac8a + b302fd3 commit e389ed6

File tree

6 files changed

+191
-94
lines changed

6 files changed

+191
-94
lines changed

src/display/annotation_layer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ class LinkAnnotationElement extends AnnotationElement {
344344
link.href = this.linkService.getDestinationHash(destination);
345345
link.onclick = () => {
346346
if (destination) {
347-
this.linkService.navigateTo(destination);
347+
this.linkService.goToDestination(destination);
348348
}
349349
return false;
350350
};

web/interfaces.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,14 @@ class IPDFLinkService {
5454
set externalLinkEnabled(value) {}
5555

5656
/**
57-
* @param dest - The PDF destination object.
57+
* @param {string|Array} dest - The named, or explicit, PDF destination.
58+
*/
59+
async goToDestination(dest) {}
60+
61+
/**
62+
* @param {number} pageNumber - The page number.
5863
*/
59-
navigateTo(dest) {}
64+
goToPage(pageNumber) {}
6065

6166
/**
6267
* @param dest - The PDF destination object.
@@ -108,6 +113,11 @@ class IPDFHistory {
108113
*/
109114
push({ namedDest = null, explicitDest, pageNumber }) {}
110115

116+
/**
117+
* @param {number} pageNumber
118+
*/
119+
pushPage(pageNumber) {}
120+
111121
pushCurrentPosition() {}
112122

113123
back() {}

web/pdf_history.js

+52-9
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,6 @@ class PDFHistory {
143143
state.uid,
144144
/* removeTemporary = */ true
145145
);
146-
if (this._uid > this._maxUid) {
147-
this._maxUid = this._uid;
148-
}
149146

150147
if (destination.rotation !== undefined) {
151148
this._initialRotation = destination.rotation;
@@ -271,6 +268,55 @@ class PDFHistory {
271268
}
272269
}
273270

271+
/**
272+
* Push a page to the browser history; generally the `push` method should be
273+
* used instead.
274+
* @param {number} pageNumber
275+
*/
276+
pushPage(pageNumber) {
277+
if (!this._initialized) {
278+
return;
279+
}
280+
if (
281+
!(
282+
Number.isInteger(pageNumber) &&
283+
pageNumber > 0 &&
284+
pageNumber <= this.linkService.pagesCount
285+
)
286+
) {
287+
console.error(
288+
`PDFHistory.pushPage: "${pageNumber}" is not a valid page number.`
289+
);
290+
return;
291+
}
292+
293+
if (this._destination && this._destination.page === pageNumber) {
294+
// When the new page is identical to the one in `this._destination`, we
295+
// don't want to add a potential duplicate entry in the browser history.
296+
return;
297+
}
298+
if (this._popStateInProgress) {
299+
return;
300+
}
301+
302+
this._pushOrReplaceState({
303+
hash: `page=${pageNumber}`,
304+
page: pageNumber,
305+
rotation: this.linkService.rotation,
306+
});
307+
308+
if (!this._popStateInProgress) {
309+
// Prevent the browser history from updating while the new page is
310+
// being scrolled into view, to avoid potentially inconsistent state.
311+
this._popStateInProgress = true;
312+
// We defer the resetting of `this._popStateInProgress`, to account for
313+
// e.g. zooming occuring when the new page is being navigated to.
314+
Promise.resolve().then(() => {
315+
this._popStateInProgress = false;
316+
});
317+
}
318+
}
319+
274320
/**
275321
* Push the current position to the browser history.
276322
*/
@@ -361,7 +407,6 @@ class PDFHistory {
361407
if (shouldReplace) {
362408
window.history.replaceState(newState, "", newUrl);
363409
} else {
364-
this._maxUid = this._uid;
365410
window.history.pushState(newState, "", newUrl);
366411
}
367412

@@ -485,6 +530,7 @@ class PDFHistory {
485530
}
486531
this._destination = destination;
487532
this._uid = uid;
533+
this._maxUid = Math.max(this._maxUid, uid);
488534
// This should always be reset when `this._destination` is updated.
489535
this._numPositionUpdates = 0;
490536
}
@@ -639,23 +685,20 @@ class PDFHistory {
639685
state.uid,
640686
/* removeTemporary = */ true
641687
);
642-
if (this._uid > this._maxUid) {
643-
this._maxUid = this._uid;
644-
}
645688

646689
if (isValidRotation(destination.rotation)) {
647690
this.linkService.rotation = destination.rotation;
648691
}
649692
if (destination.dest) {
650-
this.linkService.navigateTo(destination.dest);
693+
this.linkService.goToDestination(destination.dest);
651694
} else if (destination.hash) {
652695
this.linkService.setHash(destination.hash);
653696
} else if (destination.page) {
654697
// Fallback case; shouldn't be necessary, but better safe than sorry.
655698
this.linkService.page = destination.page;
656699
}
657700

658-
// Since `PDFLinkService.navigateTo` is asynchronous, we thus defer the
701+
// Since `PDFLinkService.goToDestination` is asynchronous, we thus defer the
659702
// resetting of `this._popStateInProgress` slightly.
660703
Promise.resolve().then(() => {
661704
this._popStateInProgress = false;

web/pdf_link_service.js

+124-80
Original file line numberDiff line numberDiff line change
@@ -108,91 +108,127 @@ class PDFLinkService {
108108
}
109109

110110
/**
111-
* @param {string|Array} dest - The named, or explicit, PDF destination.
111+
* @deprecated
112112
*/
113113
navigateTo(dest) {
114-
const goToDestination = ({ namedDest, explicitDest }) => {
115-
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
116-
const destRef = explicitDest[0];
117-
let pageNumber;
118-
119-
if (destRef instanceof Object) {
120-
pageNumber = this._cachedPageNumber(destRef);
121-
122-
if (pageNumber === null) {
123-
// Fetch the page reference if it's not yet available. This could
124-
// only occur during loading, before all pages have been resolved.
125-
this.pdfDocument
126-
.getPageIndex(destRef)
127-
.then(pageIndex => {
128-
this.cachePageRef(pageIndex + 1, destRef);
129-
goToDestination({ namedDest, explicitDest });
130-
})
131-
.catch(() => {
132-
console.error(
133-
`PDFLinkService.navigateTo: "${destRef}" is not ` +
134-
`a valid page reference, for dest="${dest}".`
135-
);
136-
});
137-
return;
138-
}
139-
} else if (Number.isInteger(destRef)) {
140-
pageNumber = destRef + 1;
141-
} else {
142-
console.error(
143-
`PDFLinkService.navigateTo: "${destRef}" is not ` +
144-
`a valid destination reference, for dest="${dest}".`
145-
);
146-
return;
147-
}
148-
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
149-
console.error(
150-
`PDFLinkService.navigateTo: "${pageNumber}" is not ` +
151-
`a valid page number, for dest="${dest}".`
152-
);
153-
return;
154-
}
155-
156-
if (this.pdfHistory) {
157-
// Update the browser history before scrolling the new destination into
158-
// view, to be able to accurately capture the current document position.
159-
this.pdfHistory.pushCurrentPosition();
160-
this.pdfHistory.push({ namedDest, explicitDest, pageNumber });
161-
}
114+
console.error(
115+
"Deprecated method: `navigateTo`, use `goToDestination` instead."
116+
);
117+
this.goToDestination(dest);
118+
}
162119

163-
this.pdfViewer.scrollPageIntoView({
164-
pageNumber,
165-
destArray: explicitDest,
166-
ignoreDestinationZoom: this._ignoreDestinationZoom,
167-
});
168-
};
169-
170-
new Promise((resolve, reject) => {
171-
if (typeof dest === "string") {
172-
this.pdfDocument.getDestination(dest).then(destArray => {
173-
resolve({
174-
namedDest: dest,
175-
explicitDest: destArray,
120+
/**
121+
* @private
122+
*/
123+
_goToDestinationHelper(rawDest, namedDest = null, explicitDest) {
124+
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
125+
const destRef = explicitDest[0];
126+
let pageNumber;
127+
128+
if (destRef instanceof Object) {
129+
pageNumber = this._cachedPageNumber(destRef);
130+
131+
if (pageNumber === null) {
132+
// Fetch the page reference if it's not yet available. This could
133+
// only occur during loading, before all pages have been resolved.
134+
this.pdfDocument
135+
.getPageIndex(destRef)
136+
.then(pageIndex => {
137+
this.cachePageRef(pageIndex + 1, destRef);
138+
this._goToDestinationHelper(rawDest, namedDest, explicitDest);
139+
})
140+
.catch(() => {
141+
console.error(
142+
`PDFLinkService._goToDestinationHelper: "${destRef}" is not ` +
143+
`a valid page reference, for dest="${rawDest}".`
144+
);
176145
});
177-
});
178-
return;
179-
}
180-
resolve({
181-
namedDest: "",
182-
explicitDest: dest,
183-
});
184-
}).then(data => {
185-
if (!Array.isArray(data.explicitDest)) {
186-
console.error(
187-
`PDFLinkService.navigateTo: "${data.explicitDest}" is` +
188-
` not a valid destination array, for dest="${dest}".`
189-
);
190146
return;
191147
}
192-
goToDestination(data);
148+
} else if (Number.isInteger(destRef)) {
149+
pageNumber = destRef + 1;
150+
} else {
151+
console.error(
152+
`PDFLinkService._goToDestinationHelper: "${destRef}" is not ` +
153+
`a valid destination reference, for dest="${rawDest}".`
154+
);
155+
return;
156+
}
157+
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
158+
console.error(
159+
`PDFLinkService._goToDestinationHelper: "${pageNumber}" is not ` +
160+
`a valid page number, for dest="${rawDest}".`
161+
);
162+
return;
163+
}
164+
165+
if (this.pdfHistory) {
166+
// Update the browser history before scrolling the new destination into
167+
// view, to be able to accurately capture the current document position.
168+
this.pdfHistory.pushCurrentPosition();
169+
this.pdfHistory.push({ namedDest, explicitDest, pageNumber });
170+
}
171+
172+
this.pdfViewer.scrollPageIntoView({
173+
pageNumber,
174+
destArray: explicitDest,
175+
ignoreDestinationZoom: this._ignoreDestinationZoom,
193176
});
194177
}
195178

179+
/**
180+
* This method will, when available, also update the browser history.
181+
*
182+
* @param {string|Array} dest - The named, or explicit, PDF destination.
183+
*/
184+
async goToDestination(dest) {
185+
let namedDest, explicitDest;
186+
if (typeof dest === "string") {
187+
namedDest = dest;
188+
explicitDest = await this.pdfDocument.getDestination(dest);
189+
} else {
190+
namedDest = null;
191+
explicitDest = await dest;
192+
}
193+
if (!Array.isArray(explicitDest)) {
194+
console.error(
195+
`PDFLinkService.goToDestination: "${explicitDest}" is not ` +
196+
`a valid destination array, for dest="${dest}".`
197+
);
198+
return;
199+
}
200+
this._goToDestinationHelper(dest, namedDest, explicitDest);
201+
}
202+
203+
/**
204+
* This method will, when available, also update the browser history.
205+
*
206+
* @param {number} pageNumber - The page number.
207+
*/
208+
goToPage(pageNumber) {
209+
if (
210+
!(
211+
Number.isInteger(pageNumber) &&
212+
pageNumber > 0 &&
213+
pageNumber <= this.pagesCount
214+
)
215+
) {
216+
console.error(
217+
`PDFLinkService.goToPage: "${pageNumber}" is not a valid page number.`
218+
);
219+
return;
220+
}
221+
222+
if (this.pdfHistory) {
223+
// Update the browser history before scrolling the new page into view,
224+
// to be able to accurately capture the current document position.
225+
this.pdfHistory.pushCurrentPosition();
226+
this.pdfHistory.pushPage(pageNumber);
227+
}
228+
229+
this.pdfViewer.scrollPageIntoView({ pageNumber });
230+
}
231+
196232
/**
197233
* @param {string|Array} dest - The PDF destination object.
198234
* @returns {string} The hyperlink to the PDF object.
@@ -307,7 +343,7 @@ class PDFLinkService {
307343
// Ensure that this parameter is *always* handled last, in order to
308344
// guarantee that it won't be overridden (e.g. by the "page" parameter).
309345
if ("nameddest" in params) {
310-
this.navigateTo(params.nameddest);
346+
this.goToDestination(params.nameddest);
311347
}
312348
} else {
313349
// Named (or explicit) destination.
@@ -323,7 +359,7 @@ class PDFLinkService {
323359
} catch (ex) {}
324360

325361
if (typeof dest === "string" || isValidExplicitDestination(dest)) {
326-
this.navigateTo(dest);
362+
this.goToDestination(dest);
327363
return;
328364
}
329365
console.error(
@@ -394,6 +430,9 @@ class PDFLinkService {
394430
this._pagesRefCache[refStr] = pageNum;
395431
}
396432

433+
/**
434+
* @private
435+
*/
397436
_cachedPageNumber(pageRef) {
398437
const refStr =
399438
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
@@ -510,9 +549,14 @@ class SimpleLinkService {
510549
set rotation(value) {}
511550

512551
/**
513-
* @param dest - The PDF destination object.
552+
* @param {string|Array} dest - The named, or explicit, PDF destination.
553+
*/
554+
async goToDestination(dest) {}
555+
556+
/**
557+
* @param {number} pageNumber - The page number.
514558
*/
515-
navigateTo(dest) {}
559+
goToPage(pageNumber) {}
516560

517561
/**
518562
* @param dest - The PDF destination object.

0 commit comments

Comments
 (0)