Coverage Report

Created: 2026-10-13 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/pdf/pdf-js.c
Line
Count
Source
1
// Copyright (C) 2004-2026 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
#include "mupdf/pdf.h"
25
26
#if FZ_ENABLE_JS
27
28
// set limits to how much memory and cpu malicious/broken scripts can use up
29
0
#define PDF_JS_LIMIT_RUNTIME (10 << 20) // ten million instructions
30
0
#define PDF_JS_LIMIT_MEMORY (100 << 20) // one hundred megabytes
31
32
#include "mujs.h"
33
34
#include <stdarg.h>
35
#include <string.h>
36
37
struct pdf_js
38
{
39
  fz_context *ctx;
40
  pdf_document *doc;
41
  js_State *imp;
42
  pdf_js_console *console;
43
  void *console_user;
44
};
45
46
FZ_NORETURN static void rethrow(pdf_js *js)
47
0
{
48
0
  js_newerror(js->imp, fz_convert_error(js->ctx, NULL));
49
0
  js_throw(js->imp);
50
0
}
51
52
/* Unpack argument object with named arguments into actual parameters. */
53
static pdf_js *unpack_arguments(js_State *J, ...)
54
0
{
55
0
  if (js_isobject(J, 1))
56
0
  {
57
0
    int i = 1;
58
0
    va_list args;
59
60
0
    js_copy(J, 1);
61
62
0
    va_start(args, J);
63
0
    for (;;)
64
0
    {
65
0
      const char *s = va_arg(args, const char *);
66
0
      if (!s)
67
0
        break;
68
0
      js_getproperty(J, -1, s);
69
0
      js_replace(J, i++);
70
0
    }
71
0
    va_end(args);
72
73
0
    js_pop(J, 1);
74
0
  }
75
0
  return js_getcontext(J);
76
0
}
77
78
static void app_alert(js_State *J)
79
0
{
80
0
  pdf_js *js = unpack_arguments(J, "cMsg", "nIcon", "nType", "cTitle", "oDoc", "oCheckbox", NULL);
81
0
  pdf_alert_event evt;
82
83
  /* TODO: Currently we do not support app.openDoc() in javascript actions, hence
84
  oDoc can only point to the current document (or not be passed). When mupdf
85
  supports opening other documents oDoc must be converted to a pdf_document * that
86
  can be passed to the callback. In the mean time, we just pas the current document.
87
  */
88
0
  evt.doc = js->doc;
89
90
0
  evt.message = js_tostring(J, 1);
91
0
  evt.icon_type = js_tointeger(J, 2);
92
0
  evt.button_group_type = js_tointeger(J, 3);
93
0
  evt.title = js_isdefined(J, 4) ? js_tostring(J, 4) : "PDF alert";
94
95
0
  evt.has_check_box = 0;
96
0
  evt.check_box_message = NULL;
97
0
  evt.initially_checked = 0;
98
0
  evt.finally_checked = 0;
99
100
0
  if (js_isobject(J, 6))
101
0
  {
102
0
    evt.has_check_box = 1;
103
0
    evt.check_box_message = "Do not show this message again";
104
0
    if (js_hasproperty(J, 6, "cMsg"))
105
0
    {
106
0
      if (js_iscoercible(J, -1))
107
0
        evt.check_box_message = js_tostring(J, -1);
108
0
      js_pop(J, 1);
109
0
    }
110
0
    if (js_hasproperty(J, 6, "bInitialValue"))
111
0
    {
112
0
      evt.initially_checked = js_tointeger(J, -1);
113
0
      js_pop(J, 1);
114
0
    }
115
0
    if (js_hasproperty(J, 6, "bAfterValue"))
116
0
    {
117
0
      evt.finally_checked = js_tointeger(J, -1);
118
0
      js_pop(J, 1);
119
0
    }
120
0
  }
121
122
  /* These are the default buttons automagically "pressed"
123
  when the dialog box window is closed in Acrobat. */
124
0
  switch (evt.button_group_type)
125
0
  {
126
0
  default:
127
0
  case PDF_ALERT_BUTTON_GROUP_OK:
128
0
    evt.button_pressed = PDF_ALERT_BUTTON_OK;
129
0
    break;
130
0
  case PDF_ALERT_BUTTON_GROUP_OK_CANCEL:
131
0
    evt.button_pressed = PDF_ALERT_BUTTON_CANCEL;
132
0
    break;
133
0
  case PDF_ALERT_BUTTON_GROUP_YES_NO:
134
0
    evt.button_pressed = PDF_ALERT_BUTTON_YES;
135
0
    break;
136
0
  case PDF_ALERT_BUTTON_GROUP_YES_NO_CANCEL:
137
0
    evt.button_pressed = PDF_ALERT_BUTTON_CANCEL;
138
0
    break;
139
0
  }
140
141
0
  fz_try(js->ctx)
142
0
    pdf_event_issue_alert(js->ctx, js->doc, &evt);
143
0
  fz_catch(js->ctx)
144
0
    rethrow(js);
145
146
0
  if (js_isobject(J, 6))
147
0
  {
148
0
    js_pushboolean(js->imp, evt.finally_checked);
149
0
    js_setproperty(js->imp, 6, "bAfterValue");
150
0
  }
151
152
0
  js_pushnumber(J, evt.button_pressed);
153
0
}
154
155
static void app_execMenuItem(js_State *J)
156
0
{
157
0
  pdf_js *js = js_getcontext(J);
158
0
  const char *cMenuItem = js_tostring(J, 1);
159
0
  fz_try(js->ctx)
160
0
    pdf_event_issue_exec_menu_item(js->ctx, js->doc, cMenuItem);
161
0
  fz_catch(js->ctx)
162
0
    rethrow(js);
163
0
}
164
165
static void app_launchURL(js_State *J)
166
0
{
167
0
  pdf_js *js = js_getcontext(J);
168
0
  const char *cUrl = js_tostring(J, 1);
169
0
  int bNewFrame = js_toboolean(J, 1);
170
0
  fz_try(js->ctx)
171
0
    pdf_event_issue_launch_url(js->ctx, js->doc, cUrl, bNewFrame);
172
0
  fz_catch(js->ctx)
173
0
    rethrow(js);
174
0
}
175
176
static void field_finalize(js_State *J, void *p)
177
0
{
178
0
  pdf_js *js = js_getcontext(J);
179
0
  pdf_drop_obj(js->ctx, p);
180
0
}
181
182
static void field_buttonSetCaption(js_State *J)
183
0
{
184
0
  pdf_js *js = js_getcontext(J);
185
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
186
0
  const char *cCaption = js_tostring(J, 1);
187
0
  fz_try(js->ctx)
188
0
    pdf_field_set_button_caption(js->ctx, field, cCaption);
189
0
  fz_catch(js->ctx)
190
0
    rethrow(js);
191
0
}
192
193
static void field_getName(js_State *J)
194
0
{
195
0
  pdf_js *js = js_getcontext(J);
196
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
197
0
  char *name = NULL;
198
0
  fz_try(js->ctx)
199
0
    name = pdf_load_field_name(js->ctx, field);
200
0
  fz_catch(js->ctx)
201
0
    rethrow(js);
202
0
  if (js_try(J)) {
203
0
    fz_free(js->ctx, name);
204
0
    js_throw(J);
205
0
  } else {
206
0
    js_pushstring(J, name);
207
0
    js_endtry(J);
208
0
    fz_free(js->ctx, name);
209
0
  }
210
0
}
211
212
static void field_setName(js_State *J)
213
0
{
214
0
  pdf_js *js = js_getcontext(J);
215
0
  fz_warn(js->ctx, "Unexpected call to field_setName");
216
0
}
217
218
static void field_getDisplay(js_State *J)
219
0
{
220
0
  pdf_js *js = js_getcontext(J);
221
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
222
0
  int display = 0;
223
0
  fz_try(js->ctx)
224
0
    display = pdf_field_display(js->ctx, field);
225
0
  fz_catch(js->ctx)
226
0
    rethrow(js);
227
0
  js_pushnumber(J, display);
228
0
}
229
230
static void field_setDisplay(js_State *J)
231
0
{
232
0
  pdf_js *js = js_getcontext(J);
233
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
234
0
  int display = js_tonumber(J, 1);
235
0
  fz_try(js->ctx)
236
0
    pdf_field_set_display(js->ctx, field, display);
237
0
  fz_catch(js->ctx)
238
0
    rethrow(js);
239
0
}
240
241
static pdf_obj *load_color(pdf_js *js, int idx)
242
0
{
243
0
  fz_context *ctx = js->ctx;
244
0
  pdf_document *doc = js->doc;
245
0
  js_State *J = js->imp;
246
247
0
  pdf_obj *color = NULL;
248
0
  int i, n;
249
0
  float c;
250
251
0
  n = js_getlength(J, idx);
252
253
  /* The only legitimate color expressed as an array of length 1
254
   * is [T], meaning transparent. Return a NULL object to represent
255
   * transparent */
256
0
  if (n <= 1)
257
0
    return NULL;
258
259
0
  fz_var(color);
260
261
0
  fz_try(ctx)
262
0
  {
263
0
    color = pdf_new_array(ctx, doc, n-1);
264
0
    for (i = 0; i < n-1; i++)
265
0
    {
266
0
      js_getindex(J, idx, i+1);
267
0
      c = js_tonumber(J, -1);
268
0
      js_pop(J, 1);
269
270
0
      pdf_array_push_real(ctx, color, c);
271
0
    }
272
0
  }
273
0
  fz_catch(ctx)
274
0
  {
275
0
    pdf_drop_obj(ctx, color);
276
0
    rethrow(js);
277
0
  }
278
279
0
  return color;
280
0
}
281
282
static void field_getFillColor(js_State *J)
283
0
{
284
0
  js_pushundefined(J);
285
0
}
286
287
static void field_setFillColor(js_State *J)
288
0
{
289
0
  pdf_js *js = js_getcontext(J);
290
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
291
0
  pdf_obj *color = load_color(js, 1);
292
0
  fz_try(js->ctx)
293
0
    pdf_field_set_fill_color(js->ctx, field, color);
294
0
  fz_always(js->ctx)
295
0
    pdf_drop_obj(js->ctx, color);
296
0
  fz_catch(js->ctx)
297
0
    rethrow(js);
298
0
}
299
300
static void field_getTextColor(js_State *J)
301
0
{
302
0
  js_pushundefined(J);
303
0
}
304
305
static void field_setTextColor(js_State *J)
306
0
{
307
0
  pdf_js *js = js_getcontext(J);
308
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
309
0
  pdf_obj *color = load_color(js, 1);
310
0
  fz_try(js->ctx)
311
0
    pdf_field_set_text_color(js->ctx, field, color);
312
0
  fz_always(js->ctx)
313
0
    pdf_drop_obj(js->ctx, color);
314
0
  fz_catch(js->ctx)
315
0
    rethrow(js);
316
0
}
317
318
static void field_getBorderStyle(js_State *J)
319
0
{
320
0
  pdf_js *js = js_getcontext(J);
321
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
322
0
  const char *border_style = NULL;
323
0
  fz_try(js->ctx)
324
0
    border_style = pdf_field_border_style(js->ctx, field);
325
0
  fz_catch(js->ctx)
326
0
    rethrow(js);
327
0
  js_pushstring(J, border_style);
328
0
}
329
330
static void field_setBorderStyle(js_State *J)
331
0
{
332
0
  pdf_js *js = js_getcontext(J);
333
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
334
0
  const char *border_style = js_tostring(J, 1);
335
0
  fz_try(js->ctx)
336
0
    pdf_field_set_border_style(js->ctx, field, border_style);
337
0
  fz_catch(js->ctx)
338
0
    rethrow(js);
339
0
}
340
341
static void field_getValue(js_State *J)
342
0
{
343
0
  pdf_js *js = js_getcontext(J);
344
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
345
0
  const char *str = NULL;
346
0
  char *end;
347
0
  double num;
348
349
0
  fz_try(js->ctx)
350
0
    str = pdf_field_value(js->ctx, field);
351
0
  fz_catch(js->ctx)
352
0
    rethrow(js);
353
354
0
  num = strtod(str, &end);
355
0
  if (*str && *end == 0)
356
0
    js_pushnumber(J, num);
357
0
  else
358
0
    js_pushstring(J, str);
359
0
}
360
361
static void field_setValue(js_State *J)
362
0
{
363
0
  pdf_js *js = js_getcontext(J);
364
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
365
0
  const char *value = js_tostring(J, 1);
366
367
0
  fz_try(js->ctx)
368
0
    (void)pdf_set_field_value(js->ctx, js->doc, field, value, 0);
369
0
  fz_catch(js->ctx)
370
0
    rethrow(js);
371
0
}
372
373
static void field_getType(js_State *J)
374
0
{
375
0
  pdf_js *js = js_getcontext(J);
376
0
  pdf_obj *field = js_touserdata(J, 0, "Field");
377
0
  const char *type;
378
379
0
  fz_try(js->ctx)
380
0
    type = pdf_field_type_string(js->ctx, field);
381
0
  fz_catch(js->ctx)
382
0
    rethrow(js);
383
384
0
  js_pushstring(J, type);
385
0
}
386
387
static void field_setType(js_State *J)
388
0
{
389
0
  pdf_js *js = js_getcontext(J);
390
0
  fz_warn(js->ctx, "Unexpected call to field_setType");
391
0
}
392
393
static void doc_getField(js_State *J)
394
0
{
395
0
  pdf_js *js = js_getcontext(J);
396
0
  fz_context *ctx = js->ctx;
397
0
  const char *cName = js_tostring(J, 1);
398
0
  pdf_obj *dict = NULL;
399
0
  pdf_obj *form = NULL;
400
401
0
  fz_try(ctx)
402
0
  {
403
0
    form = pdf_dict_getl(ctx, pdf_trailer(ctx, js->doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL);
404
0
    dict = pdf_lookup_field(ctx, form, cName);
405
0
  }
406
0
  fz_catch(ctx)
407
0
    rethrow(js);
408
409
0
  if (dict)
410
0
  {
411
0
    js_getregistry(J, "Field");
412
0
    js_newuserdata(J, "Field", pdf_keep_obj(js->ctx, dict), field_finalize);
413
0
  }
414
0
  else
415
0
  {
416
0
    js_pushnull(J);
417
0
  }
418
0
}
419
420
static void doc_getNumPages(js_State *J)
421
0
{
422
0
  pdf_js *js = js_getcontext(J);
423
0
  int pages = pdf_count_pages(js->ctx, js->doc);
424
0
  js_pushnumber(J, pages);
425
0
}
426
427
static void doc_setNumPages(js_State *J)
428
0
{
429
0
  pdf_js *js = js_getcontext(J);
430
0
  fz_warn(js->ctx, "Unexpected call to doc_setNumPages");
431
0
}
432
433
static void doc_getMetaString(js_State *J, const char *key)
434
0
{
435
0
  pdf_js *js = js_getcontext(J);
436
0
  char buf[256];
437
0
  int ret;
438
439
0
  fz_try(js->ctx)
440
0
    ret = fz_lookup_metadata(js->ctx, &js->doc->super, key, buf, nelem(buf)) > 0;
441
0
  fz_catch(js->ctx)
442
0
    rethrow(js);
443
444
0
  if (ret > 0)
445
0
    js_pushstring(J, buf);
446
0
  else
447
0
    js_pushundefined(J);
448
0
}
449
450
static void doc_setMetaString(js_State *J, const char *key)
451
0
{
452
0
  pdf_js *js = js_getcontext(J);
453
0
  const char *value = js_tostring(J, 1);
454
0
  fz_set_metadata(js->ctx, &js->doc->super, key, value);
455
0
}
456
457
static void doc_getMetaDate(js_State *J, const char *key)
458
0
{
459
0
  pdf_js *js = js_getcontext(J);
460
0
  char buf[256];
461
0
  int ret;
462
0
  double time;
463
464
0
  fz_try(js->ctx)
465
0
  {
466
0
    ret = fz_lookup_metadata(js->ctx, &js->doc->super, key, buf, nelem(buf)) > 0;
467
0
    if (ret > 0)
468
0
      time = pdf_parse_date(js->ctx, buf);
469
0
  }
470
0
  fz_catch(js->ctx)
471
0
    rethrow(js);
472
473
0
  if (ret > 0)
474
0
  {
475
0
    js_getglobal(J, "Date");
476
0
    js_pushnumber(J, time * 1000);
477
0
    js_construct(J, 1);
478
0
  }
479
0
  else
480
0
    js_pushundefined(J);
481
0
}
482
483
static void doc_setMetaDate(js_State *J, const char *key)
484
0
{
485
0
  pdf_js *js = js_getcontext(J);
486
0
  int64_t time;
487
0
  char value[40];
488
489
  // Coerce the argument into a date object and extract the time value.
490
0
  js_getglobal(J, "Date");
491
0
  js_copy(J, 1);
492
0
  js_construct(J, 1);
493
0
  time = js_tonumber(J, -1) / 1000;
494
0
  js_pop(J, 1);
495
496
0
  fz_try(js->ctx)
497
0
    if (pdf_format_date(js->ctx, time, value, nelem(value)))
498
0
      fz_set_metadata(js->ctx, &js->doc->super, key, value);
499
0
  fz_catch(js->ctx)
500
0
    rethrow(js);
501
0
}
502
503
0
static void doc_getAuthor(js_State *J) { doc_getMetaString(J, FZ_META_INFO_AUTHOR); }
504
0
static void doc_setAuthor(js_State *J) { doc_setMetaString(J, FZ_META_INFO_AUTHOR); }
505
0
static void doc_getTitle(js_State *J) { doc_getMetaString(J, FZ_META_INFO_TITLE); }
506
0
static void doc_setTitle(js_State *J) { doc_setMetaString(J, FZ_META_INFO_TITLE); }
507
0
static void doc_getSubject(js_State *J) { doc_getMetaString(J, FZ_META_INFO_SUBJECT); }
508
0
static void doc_setSubject(js_State *J) { doc_setMetaString(J, FZ_META_INFO_SUBJECT); }
509
0
static void doc_getKeywords(js_State *J) { doc_getMetaString(J, FZ_META_INFO_KEYWORDS); }
510
0
static void doc_setKeywords(js_State *J) { doc_setMetaString(J, FZ_META_INFO_KEYWORDS); }
511
0
static void doc_getCreator(js_State *J) { doc_getMetaString(J, FZ_META_INFO_CREATOR); }
512
0
static void doc_setCreator(js_State *J) { doc_setMetaString(J, FZ_META_INFO_CREATOR); }
513
0
static void doc_getProducer(js_State *J) { doc_getMetaString(J, FZ_META_INFO_PRODUCER); }
514
0
static void doc_setProducer(js_State *J) { doc_setMetaString(J, FZ_META_INFO_PRODUCER); }
515
0
static void doc_getCreationDate(js_State *J) { doc_getMetaDate(J, FZ_META_INFO_CREATIONDATE); }
516
0
static void doc_setCreationDate(js_State *J) { doc_setMetaDate(J, FZ_META_INFO_CREATIONDATE); }
517
0
static void doc_getModDate(js_State *J) { doc_getMetaDate(J, FZ_META_INFO_MODIFICATIONDATE); }
518
0
static void doc_setModDate(js_State *J) { doc_setMetaDate(J, FZ_META_INFO_MODIFICATIONDATE); }
519
520
static void doc_resetForm(js_State *J)
521
0
{
522
0
  pdf_js *js = js_getcontext(J);
523
0
  pdf_obj *field, *form;
524
0
  fz_context *ctx = js->ctx;
525
0
  int i, n;
526
527
0
  fz_try(ctx)
528
0
    form = pdf_dict_getl(ctx, pdf_trailer(ctx, js->doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL);
529
0
  fz_catch(ctx)
530
0
    rethrow(js);
531
532
  /* An array of fields has been passed in. Call pdf_reset_field on each item. */
533
0
  if (js_isarray(J, 1))
534
0
  {
535
0
    n = js_getlength(J, 1);
536
0
    for (i = 0; i < n; ++i)
537
0
    {
538
0
      js_getindex(J, 1, i);
539
0
      fz_try(ctx)
540
0
        field = pdf_lookup_field(ctx, form, js_tostring(J, -1));
541
0
      fz_catch(ctx)
542
0
        rethrow(js);
543
0
      if (field)
544
0
        pdf_field_reset(ctx, js->doc, field);
545
0
      js_pop(J, 1);
546
0
    }
547
0
  }
548
549
  /* No argument or null passed in means reset all. */
550
0
  else
551
0
  {
552
0
    fz_try(ctx)
553
0
    {
554
0
      n = pdf_array_len(ctx, form);
555
0
      for (i = 0; i < n; i++)
556
0
        pdf_field_reset(ctx, js->doc, pdf_array_get(ctx, form, i));
557
0
    }
558
0
    fz_catch(ctx)
559
0
      rethrow(js);
560
0
  }
561
0
}
562
563
static void doc_print(js_State *J)
564
0
{
565
0
  pdf_js *js = js_getcontext(J);
566
0
  fz_try(js->ctx)
567
0
    pdf_event_issue_print(js->ctx, js->doc);
568
0
  fz_catch(js->ctx)
569
0
    rethrow(js);
570
0
}
571
572
static void doc_mailDoc(js_State *J)
573
0
{
574
0
  pdf_js *js = unpack_arguments(J, "bUI", "cTo", "cCc", "cBcc", "cSubject", "cMessage", NULL);
575
0
  pdf_mail_doc_event evt;
576
577
0
  evt.ask_user = js_isdefined(J, 1) ? js_toboolean(J, 1) : 1;
578
0
  evt.to = js_tostring(J, 2);
579
0
  evt.cc = js_tostring(J, 3);
580
0
  evt.bcc = js_tostring(J, 4);
581
0
  evt.subject = js_tostring(J, 5);
582
0
  evt.message = js_tostring(J, 6);
583
584
0
  fz_try(js->ctx)
585
0
    pdf_event_issue_mail_doc(js->ctx, js->doc, &evt);
586
0
  fz_catch(js->ctx)
587
0
    rethrow(js);
588
0
}
589
590
static void doc_calculateNow(js_State *J)
591
0
{
592
0
  pdf_js *js = js_getcontext(J);
593
0
  fz_try(js->ctx)
594
0
    pdf_calculate_form(js->ctx, js->doc);
595
0
  fz_catch(js->ctx)
596
0
    rethrow(js);
597
0
}
598
599
static void console_println(js_State *J)
600
0
{
601
0
  pdf_js *js = js_getcontext(J);
602
0
  if (js->console && js->console->write)
603
0
  {
604
0
    int i, top = js_gettop(J);
605
0
    js->console->write(js->console_user, "\n");
606
0
    for (i = 1; i < top; ++i) {
607
0
      const char *s = js_tostring(J, i);
608
0
      if (i > 1)
609
0
        js->console->write(js->console_user, " ");
610
0
      js->console->write(js->console_user, s);
611
0
    }
612
0
  }
613
0
  js_pushboolean(J, 1);
614
0
}
615
616
static void console_clear(js_State *J)
617
0
{
618
0
  pdf_js *js = js_getcontext(J);
619
0
  if (js->console && js->console->clear)
620
0
    js->console->clear(js->console_user);
621
0
  js_pushundefined(J);
622
0
}
623
624
static void console_show(js_State *J)
625
0
{
626
0
  pdf_js *js = js_getcontext(J);
627
0
  if (js->console && js->console->show)
628
0
    js->console->show(js->console_user);
629
0
  js_pushundefined(J);
630
0
}
631
632
static void console_hide(js_State *J)
633
0
{
634
0
  pdf_js *js = js_getcontext(J);
635
0
  if (js->console && js->console->hide)
636
0
    js->console->hide(js->console_user);
637
0
  js_pushundefined(J);
638
0
}
639
640
static void util_printf_d(fz_context *ctx, fz_buffer *out, int ds, int sign, int pad, unsigned int w, int base, int value)
641
0
{
642
0
  static const char *digits = "0123456789abcdef";
643
0
  char buf[50];
644
0
  unsigned int a, i;
645
0
  int m = 0;
646
647
0
  if (w > sizeof buf)
648
0
    w = sizeof buf;
649
650
0
  if (value < 0)
651
0
  {
652
0
    sign = '-';
653
0
    a = -(unsigned)value; // cast to avoid UB on -INT_MIN
654
0
  }
655
0
  else
656
0
  {
657
0
    a = value;
658
0
  }
659
660
0
  i = 0;
661
0
  do
662
0
  {
663
0
    buf[i++] = digits[a % base];
664
0
    a /= base;
665
0
    if (a > 0 && ++m == 3)
666
0
    {
667
0
      if (ds == 0) buf[i++] = ',';
668
0
      if (ds == 2) buf[i++] = '.';
669
0
      m = 0;
670
0
    }
671
0
  } while (a);
672
673
0
  if (sign && w)
674
0
  {
675
0
    if (pad == '0')
676
0
      while (i < w - 1)
677
0
        buf[i++] = pad;
678
0
    buf[i++] = sign;
679
0
  }
680
0
  while (i < w)
681
0
    buf[i++] = pad;
682
683
0
  while (i > 0)
684
0
    fz_append_byte(ctx, out, buf[--i]);
685
0
}
686
687
static void util_printf_f(fz_context *ctx, fz_buffer *out, int ds, int sign, int pad, int special, unsigned int w, int p, double value)
688
0
{
689
0
  char buf[40], *point, *digits = buf;
690
0
  size_t n = 0;
691
0
  int m = 0;
692
693
0
  fz_snprintf(buf, sizeof buf, "%.*f", p, value);
694
695
0
  if (*digits == '-')
696
0
  {
697
0
    sign = '-';
698
0
    ++digits;
699
0
  }
700
701
0
  if (*digits != '.' && (*digits < '0' || *digits > '9'))
702
0
  {
703
0
    fz_append_string(ctx, out, "nan");
704
0
    return;
705
0
  }
706
707
0
  n = strlen(digits);
708
0
  if (sign)
709
0
    ++n;
710
0
  point = strchr(digits, '.');
711
0
  if (point)
712
0
    m = 3 - (point - digits) % 3;
713
0
  else
714
0
  {
715
0
    m = 3 - n % 3;
716
0
    if (special)
717
0
      ++n;
718
0
  }
719
0
  if (m == 3)
720
0
    m = 0;
721
722
0
  if (pad == '0' && sign)
723
0
    fz_append_byte(ctx, out, sign);
724
0
  for (; n < w; ++n)
725
0
    fz_append_byte(ctx, out, pad);
726
0
  if (pad == ' ' && sign)
727
0
    fz_append_byte(ctx, out, sign);
728
729
0
  while (*digits && *digits != '.')
730
0
  {
731
0
    fz_append_byte(ctx, out, *digits++);
732
0
    if (++m == 3 && *digits && *digits != '.')
733
0
    {
734
0
      if (ds == 0) fz_append_byte(ctx, out, ',');
735
0
      if (ds == 2) fz_append_byte(ctx, out, '.');
736
0
      m = 0;
737
0
    }
738
0
  }
739
740
0
  if (*digits == '.' || special)
741
0
  {
742
0
    if (ds == 0 || ds == 1)
743
0
      fz_append_byte(ctx, out, '.');
744
0
    else
745
0
      fz_append_byte(ctx, out, ',');
746
0
  }
747
748
0
  if (*digits == '.')
749
0
  {
750
0
    ++digits;
751
0
    while (*digits)
752
0
      fz_append_byte(ctx, out, *digits++);
753
0
  }
754
0
}
755
756
static void util_printf(js_State *J)
757
0
{
758
0
  pdf_js *js = js_getcontext(J);
759
0
  fz_context *ctx = js->ctx;
760
0
  const char *fmt = js_tostring(J, 1);
761
0
  fz_buffer *out = NULL;
762
0
  int ds, p, sign, pad, special;
763
0
  unsigned int w;
764
0
  int c, i = 1;
765
0
  int failed = 0;
766
0
  const char *str;
767
768
0
  fz_var(out);
769
0
  fz_try(ctx)
770
0
  {
771
0
    out = fz_new_buffer(ctx, 256);
772
773
0
    while ((c = *fmt++) != 0)
774
0
    {
775
0
      if (c == '%')
776
0
      {
777
0
        c = *fmt++;
778
779
0
        ds = 1;
780
0
        if (c == ',')
781
0
        {
782
0
          c = *fmt++;
783
0
          if (!c)
784
0
            break;
785
0
          ds = c - '0';
786
0
        }
787
788
0
        special = 0;
789
0
        sign = 0;
790
0
        pad = ' ';
791
0
        while (c == ' ' || c == '+' || c == '0' || c == '#')
792
0
        {
793
0
          if (c == '+') sign = '+';
794
0
          else if (c == ' ') sign = ' ';
795
0
          else if (c == '0') pad = '0';
796
0
          else if (c == '#') special = 1;
797
0
          c = *fmt++;
798
0
        }
799
0
        if (!pad)
800
0
          pad = ' ';
801
0
        if (!c)
802
0
          break;
803
804
0
        w = 0;
805
0
        while (c >= '0' && c <= '9')
806
0
        {
807
0
          w = w * 10 + (c - '0');
808
0
          c = *fmt++;
809
0
        }
810
        /* w is unsigned, so never < 0 */
811
0
        if (!c)
812
0
          break;
813
814
0
        p = 0;
815
0
        if (c == '.')
816
0
        {
817
0
          c = *fmt++;
818
0
          while (c >= '0' && c <= '9')
819
0
          {
820
0
            p = p * 10 + (c - '0');
821
0
            c = *fmt++;
822
0
          }
823
0
          if (p < 0)
824
0
            p = 0;
825
0
        }
826
0
        else
827
0
        {
828
0
          special = 1;
829
0
        }
830
0
        if (!c)
831
0
          break;
832
833
0
        switch (c)
834
0
        {
835
0
        case '%':
836
0
          fz_append_byte(ctx, out, '%');
837
0
          break;
838
0
        case 'x':
839
0
          util_printf_d(ctx, out, ds, sign, pad, w, 16, js_tryinteger(J, ++i, 0));
840
0
          break;
841
0
        case 'd':
842
0
          util_printf_d(ctx, out, ds, sign, pad, w, 10, js_tryinteger(J, ++i, 0));
843
0
          break;
844
0
        case 'f':
845
0
          util_printf_f(ctx, out, ds, sign, pad, special, w, p, js_trynumber(J, ++i, 0));
846
0
          break;
847
0
        case 's':
848
0
        default:
849
0
          fz_append_string(ctx, out, js_trystring(J, ++i, ""));
850
0
        }
851
0
      }
852
0
      else
853
0
      {
854
0
        fz_append_byte(ctx, out, c);
855
0
      }
856
0
    }
857
858
0
    str = fz_string_from_buffer(ctx, out);
859
0
    if (js_try(J))
860
0
    {
861
0
      failed = 1;
862
0
    }
863
0
    else
864
0
    {
865
0
      js_pushstring(J, str);
866
0
      js_endtry(J);
867
0
    }
868
0
  }
869
0
  fz_always(ctx)
870
0
    fz_drop_buffer(ctx, out);
871
0
  fz_catch(ctx)
872
0
    rethrow(js);
873
874
0
  if (failed)
875
0
    js_throw(J);
876
0
}
877
878
static void addmethod(js_State *J, const char *name, js_CFunction fun, int n)
879
0
{
880
0
  const char *realname = strchr(name, '.');
881
0
  realname = realname ? realname + 1 : name;
882
0
  js_newcfunction(J, fun, name, n);
883
0
  js_defproperty(J, -2, realname, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
884
0
}
885
886
static void addproperty(js_State *J, const char *name, js_CFunction getfun, js_CFunction setfun)
887
0
{
888
0
  const char *realname = strchr(name, '.');
889
0
  realname = realname ? realname + 1 : name;
890
0
  js_newcfunction(J, getfun, name, 0);
891
0
  js_newcfunction(J, setfun, name, 1);
892
0
  js_defaccessor(J, -3, realname, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
893
0
}
894
895
static int declare_dom(pdf_js *js)
896
0
{
897
0
  js_State *J = js->imp;
898
899
0
  if (js_try(J))
900
0
  {
901
0
    return -1;
902
0
  }
903
904
  /* Allow access to the global environment via the 'global' name */
905
0
  js_pushglobal(J);
906
0
  js_defglobal(J, "global", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
907
908
  /* Create the 'event' object */
909
0
  js_newobject(J);
910
0
  js_defglobal(J, "event", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
911
912
  /* Create the 'util' object */
913
0
  js_newobject(J);
914
0
  {
915
    // TODO: util.printd
916
    // TODO: util.printx
917
0
    addmethod(J, "util.printf", util_printf, 1);
918
0
  }
919
0
  js_defglobal(J, "util", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
920
921
  /* Create the 'app' object */
922
0
  js_newobject(J);
923
0
  {
924
#ifdef _WIN32
925
    js_pushstring(J, "WIN");
926
#elif defined(__APPLE__)
927
    js_pushstring(J, "MAC");
928
#else
929
0
    js_pushstring(J, "UNIX");
930
0
#endif
931
0
    js_defproperty(J, -2, "app.platform", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
932
933
0
    addmethod(J, "app.alert", app_alert, 6);
934
0
    addmethod(J, "app.execMenuItem", app_execMenuItem, 1);
935
0
    addmethod(J, "app.launchURL", app_launchURL, 2);
936
0
  }
937
0
  js_defglobal(J, "app", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
938
939
  /* Create the Field prototype object */
940
0
  js_newobject(J);
941
0
  {
942
0
    addproperty(J, "Field.value", field_getValue, field_setValue);
943
0
    addproperty(J, "Field.type", field_getType, field_setType);
944
0
    addproperty(J, "Field.borderStyle", field_getBorderStyle, field_setBorderStyle);
945
0
    addproperty(J, "Field.textColor", field_getTextColor, field_setTextColor);
946
0
    addproperty(J, "Field.fillColor", field_getFillColor, field_setFillColor);
947
0
    addproperty(J, "Field.display", field_getDisplay, field_setDisplay);
948
0
    addproperty(J, "Field.name", field_getName, field_setName);
949
0
    addmethod(J, "Field.buttonSetCaption", field_buttonSetCaption, 1);
950
0
  }
951
0
  js_setregistry(J, "Field");
952
953
  /* Create the console object */
954
0
  js_newobject(J);
955
0
  {
956
0
    addmethod(J, "console.println", console_println, 1);
957
0
    addmethod(J, "console.clear", console_clear, 0);
958
0
    addmethod(J, "console.show", console_show, 0);
959
0
    addmethod(J, "console.hide", console_hide, 0);
960
0
  }
961
0
  js_defglobal(J, "console", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
962
963
  /* Put all of the Doc methods in the global object, which is used as
964
   * the 'this' binding for regular non-strict function calls. */
965
0
  js_pushglobal(J);
966
0
  {
967
0
    addproperty(J, "Doc.numPages", doc_getNumPages, doc_setNumPages);
968
0
    addproperty(J, "Doc.author", doc_getAuthor, doc_setAuthor);
969
0
    addproperty(J, "Doc.title", doc_getTitle, doc_setTitle);
970
0
    addproperty(J, "Doc.subject", doc_getSubject, doc_setSubject);
971
0
    addproperty(J, "Doc.keywords", doc_getKeywords, doc_setKeywords);
972
0
    addproperty(J, "Doc.creator", doc_getCreator, doc_setCreator);
973
0
    addproperty(J, "Doc.producer", doc_getProducer, doc_setProducer);
974
0
    addproperty(J, "Doc.creationDate", doc_getCreationDate, doc_setCreationDate);
975
0
    addproperty(J, "Doc.modDate", doc_getModDate, doc_setModDate);
976
0
    addmethod(J, "Doc.getField", doc_getField, 1);
977
0
    addmethod(J, "Doc.resetForm", doc_resetForm, 0);
978
0
    addmethod(J, "Doc.calculateNow", doc_calculateNow, 0);
979
0
    addmethod(J, "Doc.print", doc_print, 0);
980
0
    addmethod(J, "Doc.mailDoc", doc_mailDoc, 6);
981
0
  }
982
0
  js_pop(J, 1);
983
984
0
  js_endtry(J);
985
986
0
  return 0;
987
0
}
988
989
static int preload_helpers(pdf_js *js)
990
0
{
991
0
  if (js_try(js->imp))
992
0
    return -1;
993
994
  /* When testing on the cluster:
995
   * Use a fixed date for "new Date" and Date.now().
996
   * Sadly, this breaks uses of the Date function without the new keyword.
997
   * Return a fixed random sequence from Math.random().
998
   */
999
#ifdef CLUSTER
1000
  js_dostring(js->imp,
1001
"var MuPDFOldDate = Date\n"
1002
"Date = function() { return new MuPDFOldDate(298252800000); }\n"
1003
"Date.now = function() { return 298252800000; }\n"
1004
"Date.UTC = function() { return 298252800000; }\n"
1005
"Date.parse = MuPDFOldDate.parse;\n"
1006
"Math.random = function() { return (Math.random.seed = Math.random.seed * 48271 % 2147483647) / 2147483647; }\n"
1007
"Math.random.seed = 217;\n"
1008
  );
1009
#endif
1010
1011
0
  js_dostring(js->imp,
1012
0
#include "js/util.js.h"
1013
0
  );
1014
1015
0
  js_endtry(js->imp);
1016
0
  return 0;
1017
0
}
1018
1019
void pdf_drop_js(fz_context *ctx, pdf_js *js)
1020
71
{
1021
71
  if (js)
1022
0
  {
1023
0
    if (js->console && js->console->drop)
1024
0
      js->console->drop(js->console, js->console_user);
1025
0
    js_freestate(js->imp);
1026
0
    fz_free(ctx, js);
1027
0
  }
1028
71
}
1029
1030
static void *pdf_js_alloc(void *actx, void *ptr, int n)
1031
0
{
1032
0
  return fz_realloc_no_throw(actx, ptr, n);
1033
0
}
1034
1035
static void default_js_console_clear(void *user)
1036
0
{
1037
0
  fz_context *ctx = user;
1038
0
  fz_write_string(ctx, fz_stddbg(ctx), "--- clear console ---\n");
1039
0
}
1040
1041
static void default_js_console_write(void *user, const char *message)
1042
0
{
1043
0
  fz_context *ctx = user;
1044
0
  fz_write_string(ctx, fz_stddbg(ctx), message);
1045
0
}
1046
1047
static pdf_js_console default_js_console = {
1048
  NULL,
1049
  NULL,
1050
  NULL,
1051
  default_js_console_clear,
1052
  default_js_console_write,
1053
};
1054
1055
static pdf_js *pdf_new_js(fz_context *ctx, pdf_document *doc)
1056
0
{
1057
0
  pdf_js *js = fz_malloc_struct(ctx, pdf_js);
1058
1059
0
  js->ctx = ctx;
1060
0
  js->doc = doc;
1061
1062
0
  fz_try(ctx)
1063
0
  {
1064
    /* Initialise the javascript engine, passing the fz_context for use in memory allocation. */
1065
0
    js->imp = js_newstate(pdf_js_alloc, ctx, 0);
1066
0
    if (!js->imp)
1067
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize javascript engine");
1068
1069
    /* Also set our pdf_js context, so we can retrieve it in callbacks. */
1070
0
    js_setcontext(js->imp, js);
1071
1072
0
    js->console = &default_js_console;
1073
0
    js->console_user = js->ctx;
1074
1075
0
    if (declare_dom(js))
1076
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize dom interface");
1077
0
    if (preload_helpers(js))
1078
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize helper functions");
1079
0
  }
1080
0
  fz_catch(ctx)
1081
0
  {
1082
0
    pdf_drop_js(ctx, js);
1083
0
    fz_rethrow(ctx);
1084
0
  }
1085
1086
0
  return js;
1087
0
}
1088
1089
static void pdf_js_load_document_level(pdf_js *js)
1090
0
{
1091
0
  fz_context *ctx = js->ctx;
1092
0
  pdf_document *doc = js->doc;
1093
0
  pdf_obj *javascript;
1094
0
  int len, i;
1095
0
  int in_op = 0;
1096
1097
0
  javascript = pdf_load_name_tree(ctx, doc, PDF_NAME(JavaScript));
1098
0
  len = pdf_dict_len(ctx, javascript);
1099
1100
0
  fz_var(in_op);
1101
1102
0
  fz_try(ctx)
1103
0
  {
1104
0
    pdf_begin_operation(ctx, doc, "Document level Javascript");
1105
0
    in_op = 1;
1106
0
    for (i = 0; i < len; i++)
1107
0
    {
1108
0
      pdf_obj *fragment = pdf_dict_get_val(ctx, javascript, i);
1109
0
      pdf_obj *code = pdf_dict_get(ctx, fragment, PDF_NAME(JS));
1110
0
      char *codebuf = pdf_load_stream_or_string_as_utf8(ctx, code);
1111
0
      char buf[100];
1112
0
      if (pdf_is_indirect(ctx, code))
1113
0
        fz_snprintf(buf, sizeof buf, "%d", pdf_to_num(ctx, code));
1114
0
      else
1115
0
        fz_snprintf(buf, sizeof buf, "Root/Names/JavaScript/Names/%d/JS", (i+1)*2);
1116
0
      pdf_js_execute(js, buf, codebuf, NULL);
1117
0
      fz_free(ctx, codebuf);
1118
0
    }
1119
0
    pdf_end_operation(ctx, doc);
1120
0
  }
1121
0
  fz_always(ctx)
1122
0
    pdf_drop_obj(ctx, javascript);
1123
0
  fz_catch(ctx)
1124
0
  {
1125
0
    if (in_op)
1126
0
      pdf_abandon_operation(ctx, doc);
1127
0
    fz_rethrow(ctx);
1128
0
  }
1129
0
}
1130
1131
void pdf_js_event_init(pdf_js *js, pdf_obj *target, const char *value, int willCommit)
1132
0
{
1133
0
  if (js)
1134
0
  {
1135
0
    js_getglobal(js->imp, "event");
1136
0
    {
1137
0
      js_pushboolean(js->imp, 1);
1138
0
      js_setproperty(js->imp, -2, "rc");
1139
1140
0
      js_pushboolean(js->imp, willCommit);
1141
0
      js_setproperty(js->imp, -2, "willCommit");
1142
1143
0
      js_getregistry(js->imp, "Field");
1144
0
      js_newuserdata(js->imp, "Field", pdf_keep_obj(js->ctx, target), field_finalize);
1145
0
      js_setproperty(js->imp, -2, "target");
1146
1147
0
      js_pushstring(js->imp, value);
1148
0
      js_setproperty(js->imp, -2, "value");
1149
0
    }
1150
0
    js_pop(js->imp, 1);
1151
0
  }
1152
0
}
1153
1154
int pdf_js_event_result(pdf_js *js)
1155
0
{
1156
0
  int rc = 1;
1157
0
  if (js)
1158
0
  {
1159
0
    js_getglobal(js->imp, "event");
1160
0
    js_getproperty(js->imp, -1, "rc");
1161
0
    rc = js_tryboolean(js->imp, -1, 1);
1162
0
    js_pop(js->imp, 2);
1163
0
  }
1164
0
  return rc;
1165
0
}
1166
1167
int pdf_js_event_result_validate(pdf_js *js, char **newtext)
1168
0
{
1169
0
  int rc = 1;
1170
0
  *newtext = NULL;
1171
0
  if (js)
1172
0
  {
1173
0
    js_getglobal(js->imp, "event");
1174
0
    js_getproperty(js->imp, -1, "rc");
1175
0
    rc = js_tryboolean(js->imp, -1, 1);
1176
0
    js_pop(js->imp, 1);
1177
0
    if (rc)
1178
0
    {
1179
0
      js_getproperty(js->imp, -1, "value");
1180
0
      *newtext = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
1181
0
      js_pop(js->imp, 1);
1182
0
    }
1183
0
    js_pop(js->imp, 1);
1184
0
  }
1185
0
  return rc;
1186
0
}
1187
1188
void pdf_js_event_init_keystroke(pdf_js *js, pdf_obj *target, pdf_keystroke_event *evt)
1189
0
{
1190
0
  if (js)
1191
0
  {
1192
0
    pdf_js_event_init(js, target, evt->value, evt->willCommit);
1193
0
    js_getglobal(js->imp, "event");
1194
0
    {
1195
0
      js_pushstring(js->imp, evt->change);
1196
0
      js_setproperty(js->imp, -2, "change");
1197
0
      js_pushnumber(js->imp, evt->selStart);
1198
0
      js_setproperty(js->imp, -2, "selStart");
1199
0
      js_pushnumber(js->imp, evt->selEnd);
1200
0
      js_setproperty(js->imp, -2, "selEnd");
1201
0
    }
1202
0
    js_pop(js->imp, 1);
1203
0
  }
1204
0
}
1205
1206
int pdf_js_event_result_keystroke(pdf_js *js, pdf_keystroke_event *evt)
1207
0
{
1208
0
  int rc = 1;
1209
0
  if (js)
1210
0
  {
1211
0
    js_getglobal(js->imp, "event");
1212
0
    {
1213
0
      js_getproperty(js->imp, -1, "rc");
1214
0
      rc = js_tryboolean(js->imp, -1, 1);
1215
0
      js_pop(js->imp, 1);
1216
0
      if (rc)
1217
0
      {
1218
0
        js_getproperty(js->imp, -1, "change");
1219
0
        evt->newChange = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
1220
0
        js_pop(js->imp, 1);
1221
0
        js_getproperty(js->imp, -1, "value");
1222
0
        evt->newValue = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
1223
0
        js_pop(js->imp, 1);
1224
0
        js_getproperty(js->imp, -1, "selStart");
1225
0
        evt->selStart = js_tryinteger(js->imp, -1, 0);
1226
0
        js_pop(js->imp, 1);
1227
0
        js_getproperty(js->imp, -1, "selEnd");
1228
0
        evt->selEnd = js_tryinteger(js->imp, -1, 0);
1229
0
        js_pop(js->imp, 1);
1230
0
      }
1231
0
    }
1232
0
    js_pop(js->imp, 1);
1233
0
  }
1234
0
  return rc;
1235
0
}
1236
1237
char *pdf_js_event_value(pdf_js *js)
1238
0
{
1239
0
  char *value = NULL;
1240
0
  if (js)
1241
0
  {
1242
0
    js_getglobal(js->imp, "event");
1243
0
    js_getproperty(js->imp, -1, "value");
1244
0
    value = fz_strdup(js->ctx, js_trystring(js->imp, -1, "undefined"));
1245
0
    js_pop(js->imp, 2);
1246
0
  }
1247
0
  return value;
1248
0
}
1249
1250
void pdf_js_execute(pdf_js *js, const char *name, const char *source, char **result)
1251
0
{
1252
0
  fz_context *ctx;
1253
0
  js_State *J;
1254
1255
0
  if (!js)
1256
0
    return;
1257
1258
0
  ctx = js->ctx;
1259
0
  J = js->imp;
1260
1261
0
  pdf_begin_implicit_operation(ctx, js->doc);
1262
0
  fz_try(ctx)
1263
0
  {
1264
0
    if (js_ploadstring(J, name, source)) {
1265
0
      if (result)
1266
0
        *result = fz_strdup(ctx, js_trystring(J, -1, "Error"));
1267
0
      js_pop(J, 1);
1268
0
    } else {
1269
0
      js_pushundefined(J);
1270
0
      js_setlimit(J, PDF_JS_LIMIT_RUNTIME, PDF_JS_LIMIT_MEMORY);
1271
0
      if (js_pcall(J, 0)) {
1272
0
        if (result)
1273
0
          *result = fz_strdup(ctx, js_trystring(J, -1, "Error"));
1274
0
        else
1275
0
          fz_write_printf(ctx, fz_stddbg(ctx), "js: %s\n", js_trystring(J, -1, "Error"));
1276
0
        js_pop(J, 1);
1277
0
      } else {
1278
0
        if (result)
1279
0
          *result = fz_strdup(ctx, js_tryrepr(J, -1, "can't convert to string"));
1280
0
        js_pop(J, 1);
1281
0
      }
1282
0
    }
1283
0
    pdf_end_operation(ctx, js->doc);
1284
0
  }
1285
0
  fz_catch(ctx)
1286
0
  {
1287
0
    pdf_abandon_operation(ctx, js->doc);
1288
0
    fz_rethrow(ctx);
1289
0
  }
1290
0
}
1291
1292
pdf_js_console *pdf_js_get_console(fz_context *ctx, pdf_document *doc)
1293
0
{
1294
0
  return (doc && doc->js) ? doc->js->console : NULL;
1295
0
}
1296
1297
void pdf_js_set_console(fz_context *ctx, pdf_document *doc, pdf_js_console *console, void *user)
1298
0
{
1299
0
  if (doc->js)
1300
0
  {
1301
0
    if (doc->js->console && doc->js->console->drop)
1302
0
      doc->js->console->drop(doc->js->console, doc->js->console_user);
1303
1304
0
    doc->js->console = console;
1305
0
    doc->js->console_user = user;
1306
0
  }
1307
0
}
1308
1309
void pdf_enable_js(fz_context *ctx, pdf_document *doc)
1310
0
{
1311
0
  if (!doc->js)
1312
0
  {
1313
0
    doc->js = pdf_new_js(ctx, doc);
1314
0
    pdf_js_load_document_level(doc->js);
1315
0
  }
1316
0
}
1317
1318
void pdf_disable_js(fz_context *ctx, pdf_document *doc)
1319
0
{
1320
0
  pdf_drop_js(ctx, doc->js);
1321
0
  doc->js = NULL;
1322
0
}
1323
1324
int pdf_js_supported(fz_context *ctx, pdf_document *doc)
1325
0
{
1326
  return doc->js != NULL;
1327
0
}
1328
1329
#else /* FZ_ENABLE_JS */
1330
1331
void pdf_drop_js(fz_context *ctx, pdf_js *js) { }
1332
void pdf_enable_js(fz_context *ctx, pdf_document *doc) { }
1333
void pdf_disable_js(fz_context *ctx, pdf_document *doc) { }
1334
int pdf_js_supported(fz_context *ctx, pdf_document *doc) { return 0; }
1335
void pdf_js_event_init(pdf_js *js, pdf_obj *target, const char *value, int willCommit) { }
1336
void pdf_js_event_init_keystroke(pdf_js *js, pdf_obj *target, pdf_keystroke_event *evt) { }
1337
int pdf_js_event_result_keystroke(pdf_js *js, pdf_keystroke_event *evt) { return 1; }
1338
int pdf_js_event_result(pdf_js *js) { return 1; }
1339
char *pdf_js_event_value(pdf_js *js) { return ""; }
1340
void pdf_js_execute(pdf_js *js, const char *name, const char *source, char **result) { }
1341
int pdf_js_event_result_validate(pdf_js *js, char **newvalue) { *newvalue=NULL; return 1; }
1342
pdf_js_console *pdf_js_get_console(fz_context *ctx, pdf_document *doc) { return NULL; }
1343
void pdf_js_set_console(fz_context *ctx, pdf_document *doc, pdf_js_console *console, void *user) { }
1344
1345
1346
#endif /* FZ_ENABLE_JS */