Skip to content

Commit a40bf37

Browse files
Merge pull request #19866 from Snuffleupagus/updateUrlHash
Avoid (most) string parsing when removing/replacing the hash property of a URL
2 parents c2d8854 + abc9522 commit a40bf37

11 files changed

Lines changed: 60 additions & 11 deletions

File tree

extensions/chromium/extension-router.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ limitations under the License.
4646
}
4747
var scheme = url.slice(0, schemeIndex).toLowerCase();
4848
if (schemes.includes(scheme)) {
49+
// NOTE: We cannot use the `updateUrlHash` function in this context.
4950
url = url.split("#", 1)[0];
5051
if (url.charAt(schemeIndex) === ":") {
5152
url = encodeURIComponent(url);

src/core/catalog.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,9 @@ class Catalog {
16071607
// NOTE: the destination is relative to the *remote* document.
16081608
const remoteDest = fetchRemoteDest(action);
16091609
if (remoteDest && typeof url === "string") {
1610+
// NOTE: We don't use the `updateUrlHash` function here, since
1611+
// the `createValidAbsoluteUrl` function (see below) already
1612+
// handles parsing and validation of the final URL.
16101613
url = /* baseUrl = */ url.split("#", 1)[0] + "#" + remoteDest;
16111614
}
16121615
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.

src/display/filter_factory.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*/
1515

1616
import { getRGB, isDataScheme, SVG_NS } from "./display_utils.js";
17-
import { unreachable, Util, warn } from "../shared/util.js";
17+
import { unreachable, updateUrlHash, Util, warn } from "../shared/util.js";
1818

1919
class BaseFilterFactory {
2020
constructor() {
@@ -143,7 +143,7 @@ class DOMFilterFactory extends BaseFilterFactory {
143143
if (isDataScheme(url)) {
144144
warn('#createUrl: ignore "data:"-URL for performance reasons.');
145145
} else {
146-
this.#baseUrl = url.split("#", 1)[0];
146+
this.#baseUrl = updateUrlHash(url, "");
147147
}
148148
}
149149
}

src/pdf.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
PermissionFlag,
4141
ResponseException,
4242
shadow,
43+
updateUrlHash,
4344
Util,
4445
VerbosityLevel,
4546
} from "./shared/util.js";
@@ -140,6 +141,7 @@ globalThis.pdfjsLib = {
140141
SupportedImageMimeTypes,
141142
TextLayer,
142143
TouchManager,
144+
updateUrlHash,
143145
Util,
144146
VerbosityLevel,
145147
version,
@@ -193,6 +195,7 @@ export {
193195
SupportedImageMimeTypes,
194196
TextLayer,
195197
TouchManager,
198+
updateUrlHash,
196199
Util,
197200
VerbosityLevel,
198201
version,

src/shared/util.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,28 @@ function createValidAbsoluteUrl(url, baseUrl = null, options = null) {
445445
return _isValidProtocol(absoluteUrl) ? absoluteUrl : null;
446446
}
447447

448+
/**
449+
* Remove, or replace, the hash property of the URL.
450+
*
451+
* @param {URL|string} url - The absolute, or relative, URL.
452+
* @param {string} hash - The hash property (use an empty string to remove it).
453+
* @param {boolean} [allowRel] - Allow relative URLs.
454+
* @returns {string} The resulting URL string.
455+
*/
456+
function updateUrlHash(url, hash, allowRel = false) {
457+
const res = URL.parse(url);
458+
if (res) {
459+
res.hash = hash;
460+
return res.href;
461+
}
462+
// Support well-formed relative URLs, necessary for `web/app.js` in GENERIC
463+
// builds, by optionally falling back to string parsing.
464+
if (allowRel && createValidAbsoluteUrl(url, "http://example.com")) {
465+
return url.split("#", 1)[0] + `${hash ? `#${hash}` : ""}`;
466+
}
467+
return "";
468+
}
469+
448470
function shadow(obj, prop, value, nonSerializable = false) {
449471
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
450472
assert(
@@ -1319,6 +1341,7 @@ export {
13191341
toHexUtil,
13201342
UnknownErrorException,
13211343
unreachable,
1344+
updateUrlHash,
13221345
utf8StringToString,
13231346
Util,
13241347
VerbosityLevel,

test/unit/pdf_spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
PermissionFlag,
3232
ResponseException,
3333
shadow,
34+
updateUrlHash,
3435
Util,
3536
VerbosityLevel,
3637
} from "../../src/shared/util.js";
@@ -117,6 +118,7 @@ const expectedAPI = Object.freeze({
117118
SupportedImageMimeTypes,
118119
TextLayer,
119120
TouchManager,
121+
updateUrlHash,
120122
Util,
121123
VerbosityLevel,
122124
version,

web/app.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import {
5757
shadow,
5858
stopEvent,
5959
TouchManager,
60+
updateUrlHash,
6061
version,
6162
} from "pdfjs-lib";
6263
import { AppOptions, OptionKind } from "./app_options.js";
@@ -943,10 +944,18 @@ const PDFViewerApplication = {
943944

944945
setTitleUsingUrl(url = "", downloadUrl = null) {
945946
this.url = url;
946-
this.baseUrl = url.split("#", 1)[0];
947+
this.baseUrl =
948+
typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")
949+
? updateUrlHash(url, "", /* allowRel = */ true)
950+
: updateUrlHash(url, "");
947951
if (downloadUrl) {
948952
this._downloadUrl =
949-
downloadUrl === url ? this.baseUrl : downloadUrl.split("#", 1)[0];
953+
// eslint-disable-next-line no-nested-ternary
954+
downloadUrl === url
955+
? this.baseUrl
956+
: typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")
957+
? updateUrlHash(downloadUrl, "", /* allowRel = */ true)
958+
: updateUrlHash(downloadUrl, "");
950959
}
951960
if (isDataScheme(url)) {
952961
this._hideViewBookmark();
@@ -1309,7 +1318,7 @@ const PDFViewerApplication = {
13091318
this.secondaryToolbar?.setPagesCount(pdfDocument.numPages);
13101319

13111320
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
1312-
const baseUrl = location.href.split("#", 1)[0];
1321+
const baseUrl = updateUrlHash(location, "");
13131322
// Ignore "data:"-URLs for performance reasons, even though it may cause
13141323
// internal links to not work perfectly in all cases (see bug 1803050).
13151324
this.pdfLinkService.setDocument(

web/app_options.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,13 @@ const defaultOptions = {
393393
},
394394
docBaseUrl: {
395395
/** @type {string} */
396-
value: typeof PDFJSDev === "undefined" ? document.URL.split("#", 1)[0] : "",
396+
value:
397+
typeof PDFJSDev === "undefined"
398+
? // NOTE: We cannot use the `updateUrlHash` function here, because of
399+
// the default preferences generation (see `gulpfile.mjs`).
400+
// However, the following line is *only* used in development mode.
401+
document.URL.split("#", 1)[0]
402+
: "",
397403
kind: OptionKind.API,
398404
},
399405
enableHWA: {

web/generic_scripting.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { getPdfFilenameFromUrl } from "pdfjs-lib";
1717

1818
async function docProperties(pdfDocument) {
1919
const url = "",
20-
baseUrl = url.split("#", 1)[0];
20+
baseUrl = "";
2121
const { info, metadata, contentDispositionFilename, contentLength } =
2222
await pdfDocument.getMetadata();
2323

web/pdf_history.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
1818

1919
import { isValidRotation, parseQueryString } from "./ui_utils.js";
20+
import { updateUrlHash } from "pdfjs-lib";
2021
import { waitOnEventOrTimeout } from "./event_utils.js";
2122

2223
// Heuristic value used when force-resetting `this._blockHashChange`.
@@ -383,10 +384,9 @@ class PDFHistory {
383384

384385
let newUrl;
385386
if (this._updateUrl && destination?.hash) {
386-
const baseUrl = document.location.href.split("#", 1)[0];
387-
// Prevent errors in Firefox.
388-
if (!baseUrl.startsWith("file://")) {
389-
newUrl = `${baseUrl}#${destination.hash}`;
387+
const { href, protocol } = document.location;
388+
if (protocol !== "file:") {
389+
newUrl = updateUrlHash(href, destination.hash);
390390
}
391391
}
392392
if (shouldReplace) {

0 commit comments

Comments
 (0)