mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-09 09:45:42 +02:00
In order to simplify m-c code, move some in pdf.js
* move set/clear|Timeout/Interval and crackURL code in pdf.js * remove the "backdoor" in the proxy (used to dispatch event) and so return the dispatch function in the initializer * remove listeners if an error occured during sandbox initialization * add support for alert and prompt in the sandbox * add a function to eval in the global scope
This commit is contained in:
parent
3447f7c703
commit
8bff4f1ea9
14 changed files with 472 additions and 265 deletions
|
@ -49,15 +49,28 @@ class App extends PDFObject {
|
|||
data.calculationOrder,
|
||||
this._objects
|
||||
);
|
||||
this._setTimeout = data.setTimeout;
|
||||
this._clearTimeout = data.clearTimeout;
|
||||
this._setInterval = data.setInterval;
|
||||
this._clearInterval = data.clearInterval;
|
||||
this._timeoutIds = null;
|
||||
this._timeoutIdsRegistry = null;
|
||||
|
||||
// used in proxy.js to check that this is the object with the backdoor
|
||||
this._isApp = true;
|
||||
this._timeoutIds = new WeakMap();
|
||||
// eslint-disable-next-line no-undef
|
||||
if (typeof FinalizationRegistry !== "undefined") {
|
||||
// About setTimeOut/setInterval return values (specs):
|
||||
// The return value of this method must be held in a
|
||||
// JavaScript variable.
|
||||
// Otherwise, the timeout object is subject to garbage-collection,
|
||||
// which would cause the clock to stop.
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
this._timeoutIdsRegistry = new FinalizationRegistry(
|
||||
this._cleanTimeout.bind(this)
|
||||
);
|
||||
} else {
|
||||
this._timeoutIdsRegistry = null;
|
||||
}
|
||||
|
||||
this._timeoutCallbackIds = new Map();
|
||||
this._timeoutCallbackId = 0;
|
||||
this._globalEval = data.globalEval;
|
||||
this._externalCall = data.externalCall;
|
||||
}
|
||||
|
||||
// This function is called thanks to the proxy
|
||||
|
@ -66,50 +79,58 @@ class App extends PDFObject {
|
|||
this._eventDispatcher.dispatch(pdfEvent);
|
||||
}
|
||||
|
||||
_registerTimeout(timeout, id, interval) {
|
||||
if (!this._timeoutIds) {
|
||||
this._timeoutIds = new WeakMap();
|
||||
// FinalizationRegistry isn't implemented in QuickJS
|
||||
// eslint-disable-next-line no-undef
|
||||
if (typeof FinalizationRegistry !== "undefined") {
|
||||
// About setTimeOut/setInterval return values (specs):
|
||||
// The return value of this method must be held in a
|
||||
// JavaScript variable.
|
||||
// Otherwise, the timeout object is subject to garbage-collection,
|
||||
// which would cause the clock to stop.
|
||||
_registerTimeoutCallback(cExpr) {
|
||||
const id = this._timeoutCallbackId++;
|
||||
this._timeoutCallbackIds.set(id, cExpr);
|
||||
return id;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
this._timeoutIdsRegistry = new FinalizationRegistry(
|
||||
([timeoutId, isInterval]) => {
|
||||
if (isInterval) {
|
||||
this._clearInterval(timeoutId);
|
||||
} else {
|
||||
this._clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
_unregisterTimeoutCallback(id) {
|
||||
this._timeoutCallbackIds.delete(id);
|
||||
}
|
||||
|
||||
_evalCallback({ callbackId, interval }) {
|
||||
const expr = this._timeoutCallbackIds.get(callbackId);
|
||||
if (!interval) {
|
||||
this._unregisterTimeoutCallback(callbackId);
|
||||
}
|
||||
this._timeoutIds.set(timeout, [id, interval]);
|
||||
if (this._timeoutIdsRegistry) {
|
||||
this._timeoutIdsRegistry.register(timeout, [id, interval]);
|
||||
|
||||
if (expr) {
|
||||
this._globalEval(expr);
|
||||
}
|
||||
}
|
||||
|
||||
_unregisterTimeout(timeout) {
|
||||
if (!this._timeoutIds || !this._timeoutIds.has(timeout)) {
|
||||
return;
|
||||
_registerTimeout(callbackId, interval) {
|
||||
const timeout = Object.create(null);
|
||||
const id = { callbackId, interval };
|
||||
this._timeoutIds.set(timeout, id);
|
||||
if (this._timeoutIdsRegistry) {
|
||||
this._timeoutIdsRegistry.register(timeout, id);
|
||||
}
|
||||
const [id, interval] = this._timeoutIds.get(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
_unregisterTimeout(timeout) {
|
||||
if (this._timeoutIdsRegistry) {
|
||||
this._timeoutIdsRegistry.unregister(timeout);
|
||||
}
|
||||
|
||||
const data = this._timeoutIds.get(timeout);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._timeoutIds.delete(timeout);
|
||||
this._cleanTimeout(data);
|
||||
}
|
||||
|
||||
_cleanTimeout({ callbackId, interval }) {
|
||||
this._unregisterTimeoutCallback(callbackId);
|
||||
|
||||
if (interval) {
|
||||
this._clearInterval(id);
|
||||
this._externalCall("clearInterval", [callbackId]);
|
||||
} else {
|
||||
this._clearTimeout(id);
|
||||
this._externalCall("clearTimeout", [callbackId]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,7 +430,7 @@ class App extends PDFObject {
|
|||
oDoc = null,
|
||||
oCheckbox = null
|
||||
) {
|
||||
this._send({ command: "alert", value: cMsg });
|
||||
this._externalCall("alert", [cMsg]);
|
||||
}
|
||||
|
||||
beep() {
|
||||
|
@ -425,11 +446,11 @@ class App extends PDFObject {
|
|||
}
|
||||
|
||||
clearInterval(oInterval) {
|
||||
this.unregisterTimeout(oInterval);
|
||||
this._unregisterTimeout(oInterval);
|
||||
}
|
||||
|
||||
clearTimeOut(oTime) {
|
||||
this.unregisterTimeout(oTime);
|
||||
this._unregisterTimeout(oTime);
|
||||
}
|
||||
|
||||
endPriv() {
|
||||
|
@ -524,8 +545,8 @@ class App extends PDFObject {
|
|||
/* Not implemented */
|
||||
}
|
||||
|
||||
response() {
|
||||
/* TODO or not */
|
||||
response(cQuestion, cTitle = "", cDefault = "", bPassword = "", cLabel = "") {
|
||||
return this._externalCall("prompt", [cQuestion, cDefault || ""]);
|
||||
}
|
||||
|
||||
setInterval(cExpr, nMilliseconds) {
|
||||
|
@ -537,11 +558,9 @@ class App extends PDFObject {
|
|||
"Second argument of app.setInterval must be a number"
|
||||
);
|
||||
}
|
||||
|
||||
const id = this._setInterval(cExpr, nMilliseconds);
|
||||
const timeout = Object.create(null);
|
||||
this._registerTimeout(timeout, id, true);
|
||||
return timeout;
|
||||
const callbackId = this._registerTimeoutCallback(cExpr);
|
||||
this._externalCall("setInterval", [callbackId, nMilliseconds]);
|
||||
return this._registerTimeout(callbackId, true);
|
||||
}
|
||||
|
||||
setTimeOut(cExpr, nMilliseconds) {
|
||||
|
@ -551,11 +570,9 @@ class App extends PDFObject {
|
|||
if (typeof nMilliseconds !== "number") {
|
||||
throw new TypeError("Second argument of app.setTimeOut must be a number");
|
||||
}
|
||||
|
||||
const id = this._setTimeout(cExpr, nMilliseconds);
|
||||
const timeout = Object.create(null);
|
||||
this._registerTimeout(timeout, id, false);
|
||||
return timeout;
|
||||
const callbackId = this._registerTimeoutCallback(cExpr);
|
||||
this._externalCall("setTimeout", [callbackId, nMilliseconds]);
|
||||
return this._registerTimeout(callbackId, false);
|
||||
}
|
||||
|
||||
trustedFunction() {
|
||||
|
|
|
@ -30,7 +30,6 @@ class InfoProxyHandler {
|
|||
class Doc extends PDFObject {
|
||||
constructor(data) {
|
||||
super(data);
|
||||
this.calculate = true;
|
||||
|
||||
this.baseURL = data.baseURL || "";
|
||||
this.calculate = true;
|
||||
|
|
|
@ -180,11 +180,7 @@ class Field extends PDFObject {
|
|||
}
|
||||
} catch (error) {
|
||||
event.rc = false;
|
||||
const value =
|
||||
`"${error.toString()}" for event ` +
|
||||
`"${eventName}" in object ${this._id}.` +
|
||||
`\n${error.stack}`;
|
||||
this._send({ command: "error", value });
|
||||
throw error;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -35,16 +35,20 @@ import { Field } from "./field.js";
|
|||
import { ProxyHandler } from "./proxy.js";
|
||||
import { Util } from "./util.js";
|
||||
|
||||
function initSandbox({ data, extra, out }) {
|
||||
const proxyHandler = new ProxyHandler(data.dispatchEventName);
|
||||
const {
|
||||
send,
|
||||
crackURL,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
setInterval,
|
||||
clearInterval,
|
||||
} = extra;
|
||||
function initSandbox(params) {
|
||||
delete globalThis.pdfjsScripting;
|
||||
|
||||
// externalCall is a function to call a function defined
|
||||
// outside the sandbox.
|
||||
// (see src/pdf.sandbox.external.js).
|
||||
const externalCall = globalThis.callExternalFunction;
|
||||
delete globalThis.callExternalFunction;
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
const globalEval = code => globalThis.eval(code);
|
||||
const send = data => externalCall("send", [data]);
|
||||
const proxyHandler = new ProxyHandler();
|
||||
const { data } = params;
|
||||
const doc = new Doc({
|
||||
send,
|
||||
...data.docInfo,
|
||||
|
@ -52,21 +56,21 @@ function initSandbox({ data, extra, out }) {
|
|||
const _document = { obj: doc, wrapped: new Proxy(doc, proxyHandler) };
|
||||
const app = new App({
|
||||
send,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
setInterval,
|
||||
clearInterval,
|
||||
globalEval,
|
||||
externalCall,
|
||||
_document,
|
||||
calculationOrder: data.calculationOrder,
|
||||
proxyHandler,
|
||||
...data.appInfo,
|
||||
});
|
||||
const util = new Util({ crackURL });
|
||||
|
||||
const util = new Util({ externalCall });
|
||||
const aform = new AForm(doc, app, util);
|
||||
|
||||
for (const [name, objs] of Object.entries(data.objects)) {
|
||||
const obj = objs[0];
|
||||
obj.send = send;
|
||||
obj.globalEval = globalEval;
|
||||
obj.doc = _document.wrapped;
|
||||
const field = new Field(obj);
|
||||
const wrapped = new Proxy(field, proxyHandler);
|
||||
|
@ -74,28 +78,44 @@ function initSandbox({ data, extra, out }) {
|
|||
app._objects[obj.id] = { obj: field, wrapped };
|
||||
}
|
||||
|
||||
out.global = Object.create(null);
|
||||
out.app = new Proxy(app, proxyHandler);
|
||||
out.color = new Proxy(new Color(), proxyHandler);
|
||||
out.console = new Proxy(new Console({ send }), proxyHandler);
|
||||
out.util = new Proxy(util, proxyHandler);
|
||||
out.border = Border;
|
||||
out.cursor = Cursor;
|
||||
out.display = Display;
|
||||
out.font = Font;
|
||||
out.highlight = Highlight;
|
||||
out.position = Position;
|
||||
out.scaleHow = ScaleHow;
|
||||
out.scaleWhen = ScaleWhen;
|
||||
out.style = Style;
|
||||
out.trans = Trans;
|
||||
out.zoomtype = ZoomType;
|
||||
globalThis.event = null;
|
||||
globalThis.global = Object.create(null);
|
||||
globalThis.app = new Proxy(app, proxyHandler);
|
||||
globalThis.doc = _document.wrapped;
|
||||
globalThis.color = new Proxy(new Color(), proxyHandler);
|
||||
globalThis.console = new Proxy(new Console({ send }), proxyHandler);
|
||||
globalThis.util = new Proxy(util, proxyHandler);
|
||||
globalThis.border = Border;
|
||||
globalThis.cursor = Cursor;
|
||||
globalThis.display = Display;
|
||||
globalThis.font = Font;
|
||||
globalThis.highlight = Highlight;
|
||||
globalThis.position = Position;
|
||||
globalThis.scaleHow = ScaleHow;
|
||||
globalThis.scaleWhen = ScaleWhen;
|
||||
globalThis.style = Style;
|
||||
globalThis.trans = Trans;
|
||||
globalThis.zoomtype = ZoomType;
|
||||
|
||||
for (const name of Object.getOwnPropertyNames(AForm.prototype)) {
|
||||
if (name !== "constructor" && !name.startsWith("_")) {
|
||||
out[name] = aform[name].bind(aform);
|
||||
globalThis[name] = aform[name].bind(aform);
|
||||
}
|
||||
}
|
||||
|
||||
const functions = {
|
||||
dispatchEvent: app._dispatchEvent.bind(app),
|
||||
timeoutCb: app._evalCallback.bind(app),
|
||||
};
|
||||
|
||||
return (name, args) => {
|
||||
try {
|
||||
functions[name](args);
|
||||
} catch (error) {
|
||||
const value = `${error.toString()}\n${error.stack}`;
|
||||
send({ command: "error", value });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { initSandbox };
|
||||
|
|
|
@ -14,18 +14,7 @@
|
|||
*/
|
||||
|
||||
class ProxyHandler {
|
||||
constructor(dispatchEventName) {
|
||||
this.dispatchEventName = dispatchEventName;
|
||||
}
|
||||
|
||||
get(obj, prop) {
|
||||
if (obj._isApp && prop === this.dispatchEventName) {
|
||||
// a backdoor to be able to call _dispatchEvent method
|
||||
// the value of 'dispatchEvent' is generated randomly
|
||||
// and injected in the code
|
||||
return obj._dispatchEvent.bind(obj);
|
||||
}
|
||||
|
||||
// script may add some properties to the object
|
||||
if (prop in obj._expandos) {
|
||||
const val = obj._expandos[prop];
|
||||
|
|
|
@ -19,7 +19,6 @@ class Util extends PDFObject {
|
|||
constructor(data) {
|
||||
super(data);
|
||||
|
||||
this._crackURL = data.crackURL;
|
||||
this._scandCache = new Map();
|
||||
this._months = [
|
||||
"January",
|
||||
|
@ -46,13 +45,9 @@ class Util extends PDFObject {
|
|||
];
|
||||
this.MILLISECONDS_IN_DAY = 86400000;
|
||||
this.MILLISECONDS_IN_WEEK = 604800000;
|
||||
}
|
||||
|
||||
crackURL(cURL) {
|
||||
if (typeof cURL !== "string") {
|
||||
throw new TypeError("First argument of util.crackURL must be a string");
|
||||
}
|
||||
return this._crackURL(cURL);
|
||||
// used with crackURL
|
||||
this._externalCall = data.externalCall;
|
||||
}
|
||||
|
||||
printf(...args) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue