[JS] Some functions (print, alert,...) must be called only after a user activation
- Some events, which require a user interaction, will allow those functions to be called. But after few seconds, if there are no more user interaction, it won't be possible anymore. The idea is to give an opportunity to the user to leave the pdf. - Disable print function when we're printing, the same with saving and disallow to save on open events.
This commit is contained in:
parent
27b251ac99
commit
0de804a256
@ -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", {
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user