Skip to content

Commit 3f29892

Browse files
committed
[JS] Fix several issues found in pdf in mozilla#13269
- app.alert and few other function can use an object as parameter ({cMsg: ...}); - support app.alert with a question and a yes/no answer; - update field siblings when one is changed in an action; - stop calculation if calculate is set to false in the middle of calculations; - get a boolean for checkboxes when they've been set through annotationStorage instead of a string.
1 parent 3f187c2 commit 3f29892

12 files changed

+223
-49
lines changed

src/display/annotation_layer.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -922,12 +922,17 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
922922
const storage = this.annotationStorage;
923923
const data = this.data;
924924
const id = data.id;
925-
const value = storage.getValue(id, {
925+
let value = storage.getValue(id, {
926926
value:
927927
data.fieldValue &&
928928
((data.exportValue && data.exportValue === data.fieldValue) ||
929929
(!data.exportValue && data.fieldValue !== "Off")),
930930
}).value;
931+
if (typeof value === "string") {
932+
// The value has been changed through js and set in annotationStorage.
933+
value = value !== "Off";
934+
storage.setValue(id, { value });
935+
}
931936

932937
this.container.className = "buttonWidgetAnnotation checkBox";
933938

@@ -1012,9 +1017,14 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
10121017
const storage = this.annotationStorage;
10131018
const data = this.data;
10141019
const id = data.id;
1015-
const value = storage.getValue(id, {
1020+
let value = storage.getValue(id, {
10161021
value: data.fieldValue === data.buttonValue,
10171022
}).value;
1023+
if (typeof value === "string") {
1024+
// The value has been changed through js and set in annotationStorage.
1025+
value = value !== data.buttonValue;
1026+
storage.setValue(id, { value });
1027+
}
10181028

10191029
const element = document.createElement("input");
10201030
element.disabled = data.readOnly;

src/pdf.sandbox.external.js

+6
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ class SandboxSupportBase {
117117
}
118118
this.win.alert(cMsg);
119119
},
120+
confirm: cMsg => {
121+
if (typeof cMsg !== "string") {
122+
return false;
123+
}
124+
return this.win.confirm(cMsg);
125+
},
120126
prompt: (cQuestion, cDefault) => {
121127
if (typeof cQuestion !== "string" || typeof cDefault !== "string") {
122128
return null;

src/scripting_api/app.js

+41-4
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ class App extends PDFObject {
2828
constructor(data) {
2929
super(data);
3030

31-
this.calculate = true;
32-
3331
this._constants = null;
3432
this._focusRect = true;
3533
this._fs = null;
@@ -68,6 +66,7 @@ class App extends PDFObject {
6866
this._timeoutCallbackId = 0;
6967
this._globalEval = data.globalEval;
7068
this._externalCall = data.externalCall;
69+
this._document = data._document;
7170
}
7271

7372
// This function is called thanks to the proxy
@@ -191,6 +190,14 @@ class App extends PDFObject {
191190
throw new Error("app.activeDocs is read-only");
192191
}
193192

193+
get calculate() {
194+
return this._document.obj.calculate;
195+
}
196+
197+
set calculate(calculate) {
198+
this._document.obj.calculate = calculate;
199+
}
200+
194201
get constants() {
195202
if (!this._constants) {
196203
this._constants = Object.freeze({
@@ -427,7 +434,21 @@ class App extends PDFObject {
427434
oDoc = null,
428435
oCheckbox = null
429436
) {
437+
if (typeof cMsg === "object") {
438+
nType = cMsg.nType;
439+
cMsg = cMsg.cMsg;
440+
}
441+
cMsg = (cMsg || "").toString();
442+
nType =
443+
typeof nType !== "number" || isNaN(nType) || nType < 0 || nType > 3
444+
? 0
445+
: nType;
446+
if (nType >= 2) {
447+
return this._externalCall("confirm", [cMsg]) ? 4 : 3;
448+
}
449+
430450
this._externalCall("alert", [cMsg]);
451+
return 1;
431452
}
432453

433454
beep() {
@@ -543,10 +564,21 @@ class App extends PDFObject {
543564
}
544565

545566
response(cQuestion, cTitle = "", cDefault = "", bPassword = "", cLabel = "") {
567+
if (typeof cQuestion === "object") {
568+
cDefault = cQuestion.cDefault;
569+
cQuestion = cQuestion.cQuestion;
570+
}
571+
cQuestion = (cQuestion || "").toString();
572+
cDefault = (cDefault || "").toString();
546573
return this._externalCall("prompt", [cQuestion, cDefault || ""]);
547574
}
548575

549-
setInterval(cExpr, nMilliseconds) {
576+
setInterval(cExpr, nMilliseconds = 0) {
577+
if (typeof cExpr === "object") {
578+
nMilliseconds = cExpr.nMilliseconds || 0;
579+
cExpr = cExpr.cExpr;
580+
}
581+
550582
if (typeof cExpr !== "string") {
551583
throw new TypeError("First argument of app.setInterval must be a string");
552584
}
@@ -560,7 +592,12 @@ class App extends PDFObject {
560592
return this._registerTimeout(callbackId, true);
561593
}
562594

563-
setTimeOut(cExpr, nMilliseconds) {
595+
setTimeOut(cExpr, nMilliseconds = 0) {
596+
if (typeof cExpr === "object") {
597+
nMilliseconds = cExpr.nMilliseconds || 0;
598+
cExpr = cExpr.cExpr;
599+
}
600+
564601
if (typeof cExpr !== "string") {
565602
throw new TypeError("First argument of app.setTimeOut must be a string");
566603
}

src/scripting_api/doc.js

+30-5
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,9 @@ class Doc extends PDFObject {
820820
}
821821

822822
getField(cName) {
823+
if (typeof cName === "object") {
824+
cName = cName.cName;
825+
}
823826
if (typeof cName !== "string") {
824827
throw new TypeError("Invalid field name: must be a string");
825828
}
@@ -852,7 +855,7 @@ class Doc extends PDFObject {
852855
}
853856
}
854857

855-
return undefined;
858+
return null;
856859
}
857860

858861
_getChildren(fieldName) {
@@ -885,6 +888,9 @@ class Doc extends PDFObject {
885888
}
886889

887890
getNthFieldName(nIndex) {
891+
if (typeof nIndex === "object") {
892+
nIndex = nIndex.nIndex;
893+
}
888894
if (typeof nIndex !== "number") {
889895
throw new TypeError("Invalid field index: must be a number");
890896
}
@@ -1020,6 +1026,18 @@ class Doc extends PDFObject {
10201026
bAnnotations = true,
10211027
printParams = null
10221028
) {
1029+
if (typeof bUI === "object") {
1030+
nStart = bUI.nStart;
1031+
nEnd = bUI.nEnd;
1032+
bSilent = bUI.bSilent;
1033+
bShrinkToFit = bUI.bShrinkToFit;
1034+
bPrintAsImage = bUI.bPrintAsImage;
1035+
bReverse = bUI.bReverse;
1036+
bAnnotations = bUI.bAnnotations;
1037+
printParams = bUI.printParams;
1038+
bUI = bUI.bUI;
1039+
}
1040+
10231041
// TODO: for now just use nStart and nEnd
10241042
// so need to see how to deal with the other params
10251043
// (if possible)
@@ -1084,15 +1102,22 @@ class Doc extends PDFObject {
10841102
}
10851103

10861104
resetForm(aFields = null) {
1105+
if (aFields && !Array.isArray(aFields) && typeof aFields === "object") {
1106+
aFields = aFields.aFields;
1107+
}
10871108
let mustCalculate = false;
10881109
if (aFields) {
10891110
for (const fieldName of aFields) {
1111+
if (!fieldName) {
1112+
continue;
1113+
}
10901114
const field = this.getField(fieldName);
1091-
if (field) {
1092-
field.value = field.defaultValue;
1093-
field.valueAsString = field.value;
1094-
mustCalculate = true;
1115+
if (!field) {
1116+
continue;
10951117
}
1118+
field.value = field.defaultValue;
1119+
field.valueAsString = field.value;
1120+
mustCalculate = true;
10961121
}
10971122
} else {
10981123
mustCalculate = this._fields.size !== 0;

src/scripting_api/event.js

+34-17
Original file line numberDiff line numberDiff line change
@@ -96,23 +96,33 @@ class EventDispatcher {
9696
}
9797
}
9898

99-
if (name === "Keystroke") {
100-
savedChange = {
101-
value: event.value,
102-
change: event.change,
103-
selStart: event.selStart,
104-
selEnd: event.selEnd,
105-
};
106-
} else if (name === "Blur" || name === "Focus") {
107-
Object.defineProperty(event, "value", {
108-
configurable: false,
109-
writable: false,
110-
enumerable: true,
111-
value: event.value,
112-
});
113-
} else if (name === "Validate") {
114-
this.runValidation(source, event);
115-
return;
99+
switch (name) {
100+
case "Keystroke":
101+
savedChange = {
102+
value: event.value,
103+
change: event.change,
104+
selStart: event.selStart,
105+
selEnd: event.selEnd,
106+
};
107+
break;
108+
case "Blur":
109+
case "Focus":
110+
Object.defineProperty(event, "value", {
111+
configurable: false,
112+
writable: false,
113+
enumerable: true,
114+
value: event.value,
115+
});
116+
break;
117+
case "Validate":
118+
this.runValidation(source, event);
119+
return;
120+
case "Action":
121+
this.runActions(source, source, event, name);
122+
if (this._document.obj.calculate) {
123+
this.runCalculate(source, event);
124+
}
125+
return;
116126
}
117127

118128
this.runActions(source, source, event, name);
@@ -143,8 +153,10 @@ class EventDispatcher {
143153
if (event.rc) {
144154
if (hasRan) {
145155
source.wrapped.value = event.value;
156+
source.wrapped.valueAsString = event.value;
146157
} else {
147158
source.obj.value = event.value;
159+
source.obj.valueAsString = event.value;
148160
}
149161

150162
if (this._document.obj.calculate) {
@@ -187,6 +199,11 @@ class EventDispatcher {
187199
continue;
188200
}
189201

202+
if (!this._document.obj.calculate) {
203+
// An action may have changed calculate value.
204+
continue;
205+
}
206+
190207
event.value = null;
191208
const target = this._objects[targetId];
192209
this.runActions(source, target, event, "Calculate");

src/scripting_api/field.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ class Field extends PDFObject {
8181
this._strokeColor = data.strokeColor || ["G", 0];
8282
this._textColor = data.textColor || ["G", 0];
8383
this._value = data.value || "";
84-
this._valueAsString = data.valueAsString;
8584
this._kidIds = data.kidIds || null;
8685
this._fieldType = getFieldType(this._actions);
86+
this._siblings = data.siblings || null;
8787

8888
this._globalEval = data.globalEval;
8989
this._appObjects = data.appObjects;
90+
91+
this.valueAsString = data.valueAsString || this._value;
9092
}
9193

9294
get currentValueIndices() {
@@ -246,6 +248,9 @@ class Field extends PDFObject {
246248
}
247249

248250
get valueAsString() {
251+
if (this._valueAsString === undefined) {
252+
this._valueAsString = this._value ? this._value.toString() : "";
253+
}
249254
return this._valueAsString;
250255
}
251256

@@ -286,6 +291,9 @@ class Field extends PDFObject {
286291
}
287292
this._buttonCaption[nFace] = cCaption;
288293
// TODO: send to the annotation layer
294+
// Right now the button is drawn on the canvas using its appearance so
295+
// update the caption means redraw...
296+
// We should probably have an html button for this annotation.
289297
}
290298

291299
buttonSetIcon(oIcon, nFace = 0) {
@@ -512,7 +520,7 @@ class RadioButtonField extends Field {
512520
}
513521

514522
set value(value) {
515-
if (value === null) {
523+
if (value === null || value === undefined) {
516524
this._value = "";
517525
}
518526
const i = this.exportValues.indexOf(value);
@@ -574,7 +582,7 @@ class CheckboxField extends RadioButtonField {
574582
}
575583

576584
set value(value) {
577-
if (value === "Off") {
585+
if (!value || value === "Off") {
578586
this._value = "Off";
579587
} else {
580588
super.value = value;

src/scripting_api/initialization.js

+22-8
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,29 @@ function initSandbox(params) {
9494
obj.doc = _document;
9595
obj.fieldPath = name;
9696
obj.appObjects = appObjects;
97+
9798
let field;
98-
if (obj.type === "radiobutton") {
99-
const otherButtons = annotations.slice(1);
100-
field = new RadioButtonField(otherButtons, obj);
101-
} else if (obj.type === "checkbox") {
102-
const otherButtons = annotations.slice(1);
103-
field = new CheckboxField(otherButtons, obj);
104-
} else {
105-
field = new Field(obj);
99+
switch (obj.type) {
100+
case "radiobutton": {
101+
const otherButtons = annotations.slice(1);
102+
field = new RadioButtonField(otherButtons, obj);
103+
break;
104+
}
105+
case "checkbox": {
106+
const otherButtons = annotations.slice(1);
107+
field = new CheckboxField(otherButtons, obj);
108+
break;
109+
}
110+
case "text":
111+
if (annotations.length <= 1) {
112+
field = new Field(obj);
113+
break;
114+
}
115+
obj.siblings = annotations.map(x => x.id).slice(1);
116+
field = new Field(obj);
117+
break;
118+
default:
119+
field = new Field(obj);
106120
}
107121

108122
const wrapped = new Proxy(field, proxyHandler);

0 commit comments

Comments
 (0)