Merge pull request #15618 from calixteman/15614

[JS] Some functions (print, alert,...) must be called only after a user activation
This commit is contained in:
calixteman 2022-10-28 21:04:42 +02:00 committed by GitHub
commit 8f80efa4ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 5 deletions

View File

@ -81,6 +81,13 @@ class SandboxSupportBase {
) {
return;
}
if (callbackId === 0) {
// This callbackId corresponds to the one used for userActivation.
// So here, we cancel the last userActivation.
this.win.clearTimeout(this.timeoutIds.get(callbackId));
}
const id = this.win.setTimeout(() => {
this.timeoutIds.delete(callbackId);
this.callSandboxFunction("timeoutCb", {

View File

@ -23,6 +23,7 @@ const VIEWER_TYPE = "PDF.js";
const VIEWER_VARIATION = "Full";
const VIEWER_VERSION = 21.00720099;
const FORMS_VERSION = 21.00720099;
const USERACTIVATION_CALLBACKID = 0;
class App extends PDFObject {
constructor(data) {
@ -45,7 +46,8 @@ class App extends PDFObject {
this._eventDispatcher = new EventDispatcher(
this._document,
data.calculationOrder,
this._objects
this._objects,
data.externalCall
);
this._timeoutIds = new WeakMap();
@ -63,7 +65,7 @@ class App extends PDFObject {
}
this._timeoutCallbackIds = new Map();
this._timeoutCallbackId = 0;
this._timeoutCallbackId = USERACTIVATION_CALLBACKID + 1;
this._globalEval = data.globalEval;
this._externalCall = data.externalCall;
}
@ -85,6 +87,11 @@ class App extends PDFObject {
}
_evalCallback({ callbackId, interval }) {
if (callbackId === USERACTIVATION_CALLBACKID) {
// Special callback id for userActivation stuff.
this._document.obj._userActivation = false;
return;
}
const expr = this._timeoutCallbackIds.get(callbackId);
if (!interval) {
this._unregisterTimeoutCallback(callbackId);
@ -429,6 +436,11 @@ class App extends PDFObject {
oDoc = null,
oCheckbox = null
) {
if (!this._document.obj._userActivation) {
return 0;
}
this._document.obj._userActivation = false;
if (cMsg && typeof cMsg === "object") {
nType = cMsg.nType;
cMsg = cMsg.cMsg;
@ -475,8 +487,18 @@ class App extends PDFObject {
}
execMenuItem(item) {
if (!this._document.obj._userActivation) {
return;
}
this._document.obj._userActivation = false;
switch (item) {
case "SaveAs":
if (this._document.obj._disableSaving) {
return;
}
this._send({ command: item });
break;
case "FirstPage":
case "LastPage":
case "NextPage":
@ -489,6 +511,9 @@ class App extends PDFObject {
this._send({ command: "zoom", value: "page-fit" });
break;
case "Print":
if (this._document.obj._disablePrinting) {
return;
}
this._send({ command: "print" });
break;
}
@ -629,4 +654,4 @@ class App extends PDFObject {
}
}
export { App };
export { App, USERACTIVATION_CALLBACKID };

View File

@ -96,6 +96,9 @@ class Doc extends PDFObject {
this._actions = createActionsMap(data.actions);
this._globalEval = data.globalEval;
this._pageActions = new Map();
this._userActivation = false;
this._disablePrinting = false;
this._disableSaving = false;
}
_dispatchDocEvent(name) {
@ -108,12 +111,27 @@ class Doc extends PDFObject {
"DidPrint",
"OpenAction",
]);
// When a pdf has just been opened it doesn't really make sense
// to save it: it's up to the user to decide if they want to do that.
// A pdf can contain an action /FooBar which will trigger a save
// even if there are no WillSave/DidSave (which are themselves triggered
// after a save).
this._disableSaving = true;
for (const actionName of this._actions.keys()) {
if (!dontRun.has(actionName)) {
this._runActions(actionName);
}
}
this._runActions("OpenAction");
this._disableSaving = false;
} else if (name === "WillPrint") {
this._disablePrinting = true;
this._runActions(name);
this._disablePrinting = false;
} else if (name === "WillSave") {
this._disableSaving = true;
this._runActions(name);
this._disableSaving = false;
} else {
this._runActions(name);
}
@ -361,6 +379,11 @@ class Doc extends PDFObject {
}
set layout(value) {
if (!this._userActivation) {
return;
}
this._userActivation = false;
if (typeof value !== "string") {
return;
}
@ -480,6 +503,11 @@ class Doc extends PDFObject {
}
set pageNum(value) {
if (!this._userActivation) {
return;
}
this._userActivation = false;
if (typeof value !== "number" || value < 0 || value >= this._numPages) {
return;
}
@ -628,6 +656,11 @@ class Doc extends PDFObject {
}
set zoomType(type) {
if (!this._userActivation) {
return;
}
this._userActivation = false;
if (typeof type !== "string") {
return;
}
@ -662,6 +695,11 @@ class Doc extends PDFObject {
}
set zoom(value) {
if (!this._userActivation) {
return;
}
this._userActivation = false;
if (typeof value !== "number" || value < 8.33 || value > 6400) {
return;
}
@ -1057,6 +1095,11 @@ class Doc extends PDFObject {
bAnnotations = true,
printParams = null
) {
if (this._disablePrinting || !this._userActivation) {
return;
}
this._userActivation = false;
if (bUI && typeof bUI === "object") {
nStart = bUI.nStart;
nEnd = bUI.nEnd;

View File

@ -13,6 +13,10 @@
* limitations under the License.
*/
import { USERACTIVATION_CALLBACKID } from "./doc.js";
const USERACTIVATION_MAXTIME_VALIDITY = 5000;
class Event {
constructor(data) {
this.change = data.change || "";
@ -39,10 +43,11 @@ class Event {
}
class EventDispatcher {
constructor(document, calculationOrder, objects) {
constructor(document, calculationOrder, objects, externalCall) {
this._document = document;
this._calculationOrder = calculationOrder;
this._objects = objects;
this._externalCall = externalCall;
this._document.obj._eventDispatcher = this;
this._isCalculating = false;
@ -66,6 +71,14 @@ class EventDispatcher {
return `${prefix}${event.change}${postfix}`;
}
userActivation() {
this._document.obj._userActivation = true;
this._externalCall("setTimeout", [
USERACTIVATION_CALLBACKID,
USERACTIVATION_MAXTIME_VALIDITY,
]);
}
dispatch(baseEvent) {
const id = baseEvent.id;
if (!(id in this._objects)) {
@ -76,19 +89,27 @@ class EventDispatcher {
event.name = baseEvent.name;
}
if (id === "doc") {
if (event.name === "Open") {
const eventName = event.name;
if (eventName === "Open") {
// Before running the Open event, we format all the fields
// (see bug 1766987).
this.formatAll();
}
if (
!["DidPrint", "DidSave", "WillPrint", "WillSave"].includes(eventName)
) {
this.userActivation();
}
this._document.obj._dispatchDocEvent(event.name);
} else if (id === "page") {
this.userActivation();
this._document.obj._dispatchPageEvent(
event.name,
baseEvent.actions,
baseEvent.pageNumber
);
} else if (id === "app" && baseEvent.name === "ResetForm") {
this.userActivation();
for (const fieldId of baseEvent.ids) {
const obj = this._objects[fieldId];
obj?.obj._reset();
@ -102,6 +123,8 @@ class EventDispatcher {
const event = (globalThis.event = new Event(baseEvent));
let savedChange;
this.userActivation();
if (source.obj._isButton()) {
source.obj._id = id;
event.value = source.obj._getExportValue(event.value);