Skip to content

Commit 7619547

Browse files
committed
XFA - Get each page asynchronously in order to avoid blocking the event loop (mozilla#14014)
1 parent 30bd5f0 commit 7619547

File tree

6 files changed

+76
-50
lines changed

6 files changed

+76
-50
lines changed

src/core/document.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -787,11 +787,11 @@ class PDFDocument {
787787
get numPages() {
788788
let num = 0;
789789
if (this.xfaFactory) {
790-
num = this.xfaFactory.numPages;
790+
num = this.xfaFactory.getNumPages();
791791
} else if (this.linearization) {
792-
num = this.linearization.numPages;
792+
num = Promise.resolve(this.linearization.numPages);
793793
} else {
794-
num = this.catalog.numPages;
794+
num = Promise.resolve(this.catalog.numPages);
795795
}
796796
return shadow(this, "numPages", num);
797797
}

src/core/worker.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,11 @@ class WorkerMessageHandler {
202202
]);
203203
}
204204

205-
const [numPages, fingerprints] = await Promise.all([
205+
const [numPagesPromise, fingerprints] = await Promise.all([
206206
pdfManager.ensureDoc("numPages"),
207207
pdfManager.ensureDoc("fingerprints"),
208208
]);
209+
const numPages = await numPagesPromise;
209210

210211
// Get htmlForXfa after numPages to avoid to create HTML twice.
211212
const htmlForXfa = isPureXfa

src/core/xfa/factory.js

+27-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
$globalData,
1919
$nodeName,
2020
$text,
21+
$toDocument,
2122
$toHTML,
2223
} from "./xfa_object.js";
2324
import { Binder } from "./bind.js";
@@ -45,9 +46,29 @@ class XFAFactory {
4546
return this.root && this.form;
4647
}
4748

48-
_createPages() {
49+
_getPages() {
50+
const iterator = this.form[$toDocument]();
51+
return new Promise((resolve, reject) => {
52+
const lambda = () => {
53+
try {
54+
const value = iterator.next();
55+
if (value.done) {
56+
resolve(value.value);
57+
} else {
58+
setTimeout(lambda, 0);
59+
}
60+
} catch (e) {
61+
reject(e);
62+
}
63+
};
64+
setTimeout(lambda, 0);
65+
});
66+
}
67+
68+
async _createPages() {
4969
try {
50-
this.pages = this.form[$toHTML]();
70+
this.pages = await this._getPages();
71+
// this.pages = this.form[$toDocument]();
5172
this.dims = this.pages.children.map(c => {
5273
const { width, height } = c.attributes.style;
5374
return [0, 0, parseInt(width), parseInt(height)];
@@ -61,9 +82,9 @@ class XFAFactory {
6182
return this.dims[pageIndex];
6283
}
6384

64-
get numPages() {
85+
async getNumPages() {
6586
if (!this.pages) {
66-
this._createPages();
87+
await this._createPages();
6788
}
6889
return this.dims.length;
6990
}
@@ -94,9 +115,9 @@ class XFAFactory {
94115
this.form[$globalData].fontFinder.add(fonts, reallyMissingFonts);
95116
}
96117

97-
getPages() {
118+
async getPages() {
98119
if (!this.pages) {
99-
this._createPages();
120+
await this._createPages();
100121
}
101122
const pages = this.pages;
102123
this.pages = null;

src/core/xfa/template.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {
5454
$setValue,
5555
$tabIndex,
5656
$text,
57+
$toDocument,
5758
$toHTML,
5859
$toStyle,
5960
$uid,
@@ -5395,7 +5396,7 @@ class Template extends XFAObject {
53955396
return searchNode(this, container, expr, true, true);
53965397
}
53975398

5398-
[$toHTML]() {
5399+
*[$toDocument]() {
53995400
if (!this.subform.children.length) {
54005401
return HTMLResult.success({
54015402
name: "div",
@@ -5641,6 +5642,7 @@ class Template extends XFAObject {
56415642
}
56425643
}
56435644
pageArea = targetPageArea || pageArea[$getNextPage]();
5645+
yield null;
56445646
}
56455647
}
56465648
}

src/core/xfa/xfa_object.js

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const $setSetAttributes = Symbol();
8484
const $setValue = Symbol();
8585
const $tabIndex = Symbol();
8686
const $text = Symbol();
87+
const $toDocument = Symbol();
8788
const $toHTML = Symbol();
8889
const $toString = Symbol();
8990
const $toStyle = Symbol();
@@ -1136,6 +1137,7 @@ export {
11361137
$setValue,
11371138
$tabIndex,
11381139
$text,
1140+
$toDocument,
11391141
$toHTML,
11401142
$toString,
11411143
$toStyle,

test/unit/xfa_tohtml_spec.js

+39-39
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe("XFAFactory", function () {
3939
}
4040

4141
describe("toHTML", function () {
42-
it("should convert some basic properties to CSS", function () {
42+
it("should convert some basic properties to CSS", async () => {
4343
const xml = `
4444
<?xml version="1.0"?>
4545
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -86,9 +86,9 @@ describe("XFAFactory", function () {
8686
const factory = new XFAFactory({ "xdp:xdp": xml });
8787
factory.setFonts([]);
8888

89-
expect(factory.numPages).toEqual(2);
89+
expect(await factory.getNumPages()).toEqual(2);
9090

91-
const pages = factory.getPages();
91+
const pages = await factory.getPages();
9292
const page1 = pages.children[0];
9393
expect(page1.attributes.style).toEqual({
9494
height: "789px",
@@ -144,7 +144,7 @@ describe("XFAFactory", function () {
144144
);
145145
});
146146

147-
it("should have an alt attribute from toolTip", function () {
147+
it("should have an alt attribute from toolTip", async () => {
148148
if (isNodeJS) {
149149
pending("Image is not supported in Node.js.");
150150
}
@@ -174,15 +174,15 @@ describe("XFAFactory", function () {
174174
`;
175175
const factory = new XFAFactory({ "xdp:xdp": xml });
176176

177-
expect(factory.numPages).toEqual(1);
177+
expect(await factory.getNumPages()).toEqual(1);
178178

179-
const pages = factory.getPages();
179+
const pages = await factory.getPages();
180180
const field = searchHtmlNode(pages, "name", "img");
181181

182182
expect(field.attributes.alt).toEqual("alt text");
183183
});
184184

185-
it("should have a aria heading role and level", function () {
185+
it("should have a aria heading role and level", async () => {
186186
const xml = `
187187
<?xml version="1.0"?>
188188
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -208,9 +208,9 @@ describe("XFAFactory", function () {
208208
`;
209209
const factory = new XFAFactory({ "xdp:xdp": xml });
210210

211-
expect(factory.numPages).toEqual(1);
211+
expect(await factory.getNumPages()).toEqual(1);
212212

213-
const pages = factory.getPages();
213+
const pages = await factory.getPages();
214214
const page1 = pages.children[0];
215215
const wrapper = page1.children[0];
216216
const draw = wrapper.children[0];
@@ -219,7 +219,7 @@ describe("XFAFactory", function () {
219219
expect(draw.attributes["aria-level"]).toEqual("2");
220220
});
221221

222-
it("should have aria table role", function () {
222+
it("should have aria table role", async () => {
223223
const xml = `
224224
<?xml version="1.0"?>
225225
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -263,9 +263,9 @@ describe("XFAFactory", function () {
263263
const factory = new XFAFactory({ "xdp:xdp": xml });
264264
factory.setFonts([]);
265265

266-
expect(factory.numPages).toEqual(1);
266+
expect(await factory.getNumPages()).toEqual(1);
267267

268-
const pages = factory.getPages();
268+
const pages = await factory.getPages();
269269
const table = searchHtmlNode(
270270
pages,
271271
"xfaName",
@@ -303,7 +303,7 @@ describe("XFAFactory", function () {
303303
expect(cell.attributes.role).toEqual("cell");
304304
});
305305

306-
it("should have a maxLength property", function () {
306+
it("should have a maxLength property", async () => {
307307
const xml = `
308308
<?xml version="1.0"?>
309309
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -336,15 +336,15 @@ describe("XFAFactory", function () {
336336
`;
337337
const factory = new XFAFactory({ "xdp:xdp": xml });
338338

339-
expect(factory.numPages).toEqual(1);
339+
expect(await factory.getNumPages()).toEqual(1);
340340

341-
const pages = factory.getPages();
341+
const pages = await factory.getPages();
342342
const field = searchHtmlNode(pages, "name", "input");
343343

344344
expect(field.attributes.maxLength).toEqual(123);
345345
});
346346

347-
it("should have an aria-label property from speak", function () {
347+
it("should have an aria-label property from speak", async () => {
348348
const xml = `
349349
<?xml version="1.0"?>
350350
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -378,15 +378,15 @@ describe("XFAFactory", function () {
378378
`;
379379
const factory = new XFAFactory({ "xdp:xdp": xml });
380380

381-
expect(factory.numPages).toEqual(1);
381+
expect(await factory.getNumPages()).toEqual(1);
382382

383-
const pages = factory.getPages();
383+
const pages = await factory.getPages();
384384
const field = searchHtmlNode(pages, "name", "input");
385385

386386
expect(field.attributes["aria-label"]).toEqual("Screen Reader");
387387
});
388388

389-
it("should have an aria-label property from toolTip", function () {
389+
it("should have an aria-label property from toolTip", async () => {
390390
const xml = `
391391
<?xml version="1.0"?>
392392
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -420,15 +420,15 @@ describe("XFAFactory", function () {
420420
`;
421421
const factory = new XFAFactory({ "xdp:xdp": xml });
422422

423-
expect(factory.numPages).toEqual(1);
423+
expect(await factory.getNumPages()).toEqual(1);
424424

425-
const pages = factory.getPages();
425+
const pages = await factory.getPages();
426426
const field = searchHtmlNode(pages, "name", "input");
427427

428428
expect(field.attributes["aria-label"]).toEqual("Screen Reader");
429429
});
430430

431-
it("should have an input or textarea", function () {
431+
it("should have an input or textarea", async () => {
432432
const xml = `
433433
<?xml version="1.0"?>
434434
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -463,9 +463,9 @@ describe("XFAFactory", function () {
463463
`;
464464
const factory = new XFAFactory({ "xdp:xdp": xml });
465465

466-
expect(factory.numPages).toEqual(1);
466+
expect(await factory.getNumPages()).toEqual(1);
467467

468-
const pages = factory.getPages();
468+
const pages = await factory.getPages();
469469
const field1 = searchHtmlNode(pages, "name", "input");
470470
expect(field1).not.toEqual(null);
471471

@@ -474,7 +474,7 @@ describe("XFAFactory", function () {
474474
});
475475
});
476476

477-
it("should have an input or textarea", function () {
477+
it("should have an input or textarea", async () => {
478478
const xml = `
479479
<?xml version="1.0"?>
480480
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -517,15 +517,15 @@ describe("XFAFactory", function () {
517517
`;
518518
const factory = new XFAFactory({ "xdp:xdp": xml });
519519

520-
expect(factory.numPages).toEqual(1);
520+
expect(await factory.getNumPages()).toEqual(1);
521521

522-
const pages = factory.getPages();
522+
const pages = await factory.getPages();
523523
const field1 = searchHtmlNode(pages, "name", "input");
524524
expect(field1).not.toEqual(null);
525525
expect(field1.attributes.value).toEqual("123");
526526
});
527527

528-
it("should parse URLs correctly", function () {
528+
it("should parse URLs correctly", async () => {
529529
function getXml(href) {
530530
return `
531531
<?xml version="1.0"?>
@@ -560,38 +560,38 @@ describe("XFAFactory", function () {
560560

561561
// A valid, and complete, URL.
562562
factory = new XFAFactory({ "xdp:xdp": getXml("https://www.example.com/") });
563-
expect(factory.numPages).toEqual(1);
564-
pages = factory.getPages();
563+
expect(await factory.getNumPages()).toEqual(1);
564+
pages = await factory.getPages();
565565
a = searchHtmlNode(pages, "name", "a");
566566
expect(a.value).toEqual("https://www.example.com/");
567567
expect(a.attributes.href).toEqual("https://www.example.com/");
568568

569569
// A valid, but incomplete, URL.
570570
factory = new XFAFactory({ "xdp:xdp": getXml("www.example.com/") });
571-
expect(factory.numPages).toEqual(1);
572-
pages = factory.getPages();
571+
expect(await factory.getNumPages()).toEqual(1);
572+
pages = await factory.getPages();
573573
a = searchHtmlNode(pages, "name", "a");
574574
expect(a.value).toEqual("www.example.com/");
575575
expect(a.attributes.href).toEqual("http://www.example.com/");
576576

577577
// A valid email-address.
578578
factory = new XFAFactory({ "xdp:xdp": getXml("mailto:[email protected]") });
579-
expect(factory.numPages).toEqual(1);
580-
pages = factory.getPages();
579+
expect(await factory.getNumPages()).toEqual(1);
580+
pages = await factory.getPages();
581581
a = searchHtmlNode(pages, "name", "a");
582582
expect(a.value).toEqual("mailto:[email protected]");
583583
expect(a.attributes.href).toEqual("mailto:[email protected]");
584584

585585
// Not a valid URL.
586586
factory = new XFAFactory({ "xdp:xdp": getXml("qwerty/") });
587-
expect(factory.numPages).toEqual(1);
588-
pages = factory.getPages();
587+
expect(await factory.getNumPages()).toEqual(1);
588+
pages = await factory.getPages();
589589
a = searchHtmlNode(pages, "name", "a");
590590
expect(a.value).toEqual("qwerty/");
591591
expect(a.attributes.href).toEqual("");
592592
});
593593

594-
it("should replace button with an URL by a link", function () {
594+
it("should replace button with an URL by a link", async () => {
595595
const xml = `
596596
<?xml version="1.0"?>
597597
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
@@ -635,9 +635,9 @@ describe("XFAFactory", function () {
635635
`;
636636
const factory = new XFAFactory({ "xdp:xdp": xml });
637637

638-
expect(factory.numPages).toEqual(1);
638+
expect(await factory.getNumPages()).toEqual(1);
639639

640-
const pages = factory.getPages();
640+
const pages = await factory.getPages();
641641
let a = searchHtmlNode(pages, "name", "a");
642642
expect(a.attributes.href).toEqual("https://github.com/mozilla/pdf.js");
643643
expect(a.attributes.newWindow).toEqual(true);

0 commit comments

Comments
 (0)