Coverage Report

Created: 2026-05-16 07:03

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
0
        if (w < 0)
811
0
          w = 0;
812
0
        if (!c)
813
0
          break;
814
815
0
        p = 0;
816
0
        if (c == '.')
817
0
        {
818
0
          c = *fmt++;
819
0
          while (c >= '0' && c <= '9')
820
0
          {
821
0
            p = p * 10 + (c - '0');
822
0
            c = *fmt++;
823
0
          }
824
0
          if (p < 0)
825
0
            p = 0;
826
0
        }
827
0
        else
828
0
        {
829
0
          special = 1;
830
0
        }
831
0
        if (!c)
832
0
          break;
833
834
0
        switch (c)
835
0
        {
836
0
        case '%':
837
0
          fz_append_byte(ctx, out, '%');
838
0
          break;
839
0
        case 'x':
840
0
          util_printf_d(ctx, out, ds, sign, pad, w, 16, js_tryinteger(J, ++i, 0));
841
0
          break;
842
0
        case 'd':
843
0
          util_printf_d(ctx, out, ds, sign, pad, w, 10, js_tryinteger(J, ++i, 0));
844
0
          break;
845
0
        case 'f':
846
0
          util_printf_f(ctx, out, ds, sign, pad, special, w, p, js_trynumber(J, ++i, 0));
847
0
          break;
848
0
        case 's':
849
0
        default:
850
0
          fz_append_string(ctx, out, js_trystring(J, ++i, ""));
851
0
        }
852
0
      }
853
0
      else
854
0
      {
855
0
        fz_append_byte(ctx, out, c);
856
0
      }
857
0
    }
858
859
0
    str = fz_string_from_buffer(ctx, out);
860
0
    if (js_try(J))
861
0
    {
862
0
      failed = 1;
863
0
    }
864
0
    else
865
0
    {
866
0
      js_pushstring(J, str);
867
0
      js_endtry(J);
868
0
    }
869
0
  }
870
0
  fz_always(ctx)
871
0
    fz_drop_buffer(ctx, out);
872
0
  fz_catch(ctx)
873
0
    rethrow(js);
874
875
0
  if (failed)
876
0
    js_throw(J);
877
0
}
878
879
static void addmethod(js_State *J, const char *name, js_CFunction fun, int n)
880
0
{
881
0
  const char *realname = strchr(name, '.');
882
0
  realname = realname ? realname + 1 : name;
883
0
  js_newcfunction(J, fun, name, n);
884
0
  js_defproperty(J, -2, realname, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
885
0
}
886
887
static void addproperty(js_State *J, const char *name, js_CFunction getfun, js_CFunction setfun)
888
0
{
889
0
  const char *realname = strchr(name, '.');
890
0
  realname = realname ? realname + 1 : name;
891
0
  js_newcfunction(J, getfun, name, 0);
892
0
  js_newcfunction(J, setfun, name, 1);
893
0
  js_defaccessor(J, -3, realname, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
894
0
}
895
896
static int declare_dom(pdf_js *js)
897
0
{
898
0
  js_State *J = js->imp;
899
900
0
  if (js_try(J))
901
0
  {
902
0
    return -1;
903
0
  }
904
905
  /* Allow access to the global environment via the 'global' name */
906
0
  js_pushglobal(J);
907
0
  js_defglobal(J, "global", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
908
909
  /* Create the 'event' object */
910
0
  js_newobject(J);
911
0
  js_defglobal(J, "event", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
912
913
  /* Create the 'util' object */
914
0
  js_newobject(J);
915
0
  {
916
    // TODO: util.printd
917
    // TODO: util.printx
918
0
    addmethod(J, "util.printf", util_printf, 1);
919
0
  }
920
0
  js_defglobal(J, "util", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
921
922
  /* Create the 'app' object */
923
0
  js_newobject(J);
924
0
  {
925
#ifdef _WIN32
926
    js_pushstring(J, "WIN");
927
#elif defined(__APPLE__)
928
    js_pushstring(J, "MAC");
929
#else
930
0
    js_pushstring(J, "UNIX");
931
0
#endif
932
0
    js_defproperty(J, -2, "app.platform", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
933
934
0
    addmethod(J, "app.alert", app_alert, 6);
935
0
    addmethod(J, "app.execMenuItem", app_execMenuItem, 1);
936
0
    addmethod(J, "app.launchURL", app_launchURL, 2);
937
0
  }
938
0
  js_defglobal(J, "app", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
939
940
  /* Create the Field prototype object */
941
0
  js_newobject(J);
942
0
  {
943
0
    addproperty(J, "Field.value", field_getValue, field_setValue);
944
0
    addproperty(J, "Field.type", field_getType, field_setType);
945
0
    addproperty(J, "Field.borderStyle", field_getBorderStyle, field_setBorderStyle);
946
0
    addproperty(J, "Field.textColor", field_getTextColor, field_setTextColor);
947
0
    addproperty(J, "Field.fillColor", field_getFillColor, field_setFillColor);
948
0
    addproperty(J, "Field.display", field_getDisplay, field_setDisplay);
949
0
    addproperty(J, "Field.name", field_getName, field_setName);
950
0
    addmethod(J, "Field.buttonSetCaption", field_buttonSetCaption, 1);
951
0
  }
952
0
  js_setregistry(J, "Field");
953
954
  /* Create the console object */
955
0
  js_newobject(J);
956
0
  {
957
0
    addmethod(J, "console.println", console_println, 1);
958
0
    addmethod(J, "console.clear", console_clear, 0);
959
0
    addmethod(J, "console.show", console_show, 0);
960
0
    addmethod(J, "console.hide", console_hide, 0);
961
0
  }
962
0
  js_defglobal(J, "console", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
963
964
  /* Put all of the Doc methods in the global object, which is used as
965
   * the 'this' binding for regular non-strict function calls. */
966
0
  js_pushglobal(J);
967
0
  {
968
0
    addproperty(J, "Doc.numPages", doc_getNumPages, doc_setNumPages);
969
0
    addproperty(J, "Doc.author", doc_getAuthor, doc_setAuthor);
970
0
    addproperty(J, "Doc.title", doc_getTitle, doc_setTitle);
971
0
    addproperty(J, "Doc.subject", doc_getSubject, doc_setSubject);
972
0
    addproperty(J, "Doc.keywords", doc_getKeywords, doc_setKeywords);
973
0
    addproperty(J, "Doc.creator", doc_getCreator, doc_setCreator);
974
0
    addproperty(J, "Doc.producer", doc_getProducer, doc_setProducer);
975
0
    addproperty(J, "Doc.creationDate", doc_getCreationDate, doc_setCreationDate);
976
0
    addproperty(J, "Doc.modDate", doc_getModDate, doc_setModDate);
977
0
    addmethod(J, "Doc.getField", doc_getField, 1);
978
0
    addmethod(J, "Doc.resetForm", doc_resetForm, 0);
979
0
    addmethod(J, "Doc.calculateNow", doc_calculateNow, 0);
980
0
    addmethod(J, "Doc.print", doc_print, 0);
981
0
    addmethod(J, "Doc.mailDoc", doc_mailDoc, 6);
982
0
  }
983
0
  js_pop(J, 1);
984
985
0
  js_endtry(J);
986
987
0
  return 0;
988
0
}
989
990
static int preload_helpers(pdf_js *js)
991
0
{
992
0
  if (js_try(js->imp))
993
0
    return -1;
994
995
  /* When testing on the cluster:
996
   * Use a fixed date for "new Date" and Date.now().
997
   * Sadly, this breaks uses of the Date function without the new keyword.
998
   * Return a fixed random sequence from Math.random().
999
   */
1000
#ifdef CLUSTER
1001
  js_dostring(js->imp,
1002
"var MuPDFOldDate = Date\n"
1003
"Date = function() { return new MuPDFOldDate(298252800000); }\n"
1004
"Date.now = function() { return 298252800000; }\n"
1005
"Date.UTC = function() { return 298252800000; }\n"
1006
"Date.parse = MuPDFOldDate.parse;\n"
1007
"Math.random = function() { return (Math.random.seed = Math.random.seed * 48271 % 2147483647) / 2147483647; }\n"
1008
"Math.random.seed = 217;\n"
1009
  );
1010
#endif
1011
1012
0
  js_dostring(js->imp,
1013
0
#include "js/util.js.h"
1014
0
  );
1015
1016
0
  js_endtry(js->imp);
1017
0
  return 0;
1018
0
}
1019
1020
void pdf_drop_js(fz_context *ctx, pdf_js *js)
1021
67
{
1022
67
  if (js)
1023
0
  {
1024
0
    if (js->console && js->console->drop)
1025
0
      js->console->drop(js->console, js->console_user);
1026
0
    js_freestate(js->imp);
1027
0
    fz_free(ctx, js);
1028
0
  }
1029
67
}
1030
1031
static void *pdf_js_alloc(void *actx, void *ptr, int n)
1032
0
{
1033
0
  return fz_realloc_no_throw(actx, ptr, n);
1034
0
}
1035
1036
static void default_js_console_clear(void *user)
1037
0
{
1038
0
  fz_context *ctx = user;
1039
0
  fz_write_string(ctx, fz_stddbg(ctx), "--- clear console ---\n");
1040
0
}
1041
1042
static void default_js_console_write(void *user, const char *message)
1043
0
{
1044
0
  fz_context *ctx = user;
1045
0
  fz_write_string(ctx, fz_stddbg(ctx), message);
1046
0
}
1047
1048
static pdf_js_console default_js_console = {
1049
  NULL,
1050
  NULL,
1051
  NULL,
1052
  default_js_console_clear,
1053
  default_js_console_write,
1054
};
1055
1056
static pdf_js *pdf_new_js(fz_context *ctx, pdf_document *doc)
1057
0
{
1058
0
  pdf_js *js = fz_malloc_struct(ctx, pdf_js);
1059
1060
0
  js->ctx = ctx;
1061
0
  js->doc = doc;
1062
1063
0
  fz_try(ctx)
1064
0
  {
1065
    /* Initialise the javascript engine, passing the fz_context for use in memory allocation. */
1066
0
    js->imp = js_newstate(pdf_js_alloc, ctx, 0);
1067
0
    if (!js->imp)
1068
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize javascript engine");
1069
1070
    /* Also set our pdf_js context, so we can retrieve it in callbacks. */
1071
0
    js_setcontext(js->imp, js);
1072
1073
0
    js->console = &default_js_console;
1074
0
    js->console_user = js->ctx;
1075
1076
0
    if (declare_dom(js))
1077
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize dom interface");
1078
0
    if (preload_helpers(js))
1079
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot initialize helper functions");
1080
0
  }
1081
0
  fz_catch(ctx)
1082
0
  {
1083
0
    pdf_drop_js(ctx, js);
1084
0
    fz_rethrow(ctx);
1085
0
  }
1086
1087
0
  return js;
1088
0
}
1089
1090
static void pdf_js_load_document_level(pdf_js *js)
1091
0
{
1092
0
  fz_context *ctx = js->ctx;
1093
0
  pdf_document *doc = js->doc;
1094
0
  pdf_obj *javascript;
1095
0
  int len, i;
1096
0
  int in_op = 0;
1097
1098
0
  javascript = pdf_load_name_tree(ctx, doc, PDF_NAME(JavaScript));
1099
0
  len = pdf_dict_len(ctx, javascript);
1100
1101
0
  fz_var(in_op);
1102
1103
0
  fz_try(ctx)
1104
0
  {
1105
0
    pdf_begin_operation(ctx, doc, "Document level Javascript");
1106
0
    in_op = 1;
1107
0
    for (i = 0; i < len; i++)
1108
0
    {
1109
0
      pdf_obj *fragment = pdf_dict_get_val(ctx, javascript, i);
1110
0
      pdf_obj *code = pdf_dict_get(ctx, fragment, PDF_NAME(JS));
1111
0
      char *codebuf = pdf_load_stream_or_string_as_utf8(ctx, code);
1112
0
      char buf[100];
1113
0
      if (pdf_is_indirect(ctx, code))
1114
0
        fz_snprintf(buf, sizeof buf, "%d", pdf_to_num(ctx, code));
1115
0
      else
1116
0
        fz_snprintf(buf, sizeof buf, "Root/Names/JavaScript/Names/%d/JS", (i+1)*2);
1117
0
      pdf_js_execute(js, buf, codebuf, NULL);
1118
0
      fz_free(ctx, codebuf);
1119
0
    }
1120
0
    pdf_end_operation(ctx, doc);
1121
0
  }
1122
0
  fz_always(ctx)
1123
0
    pdf_drop_obj(ctx, javascript);
1124
0
  fz_catch(ctx)
1125
0
  {
1126
0
    if (in_op)
1127
0
      pdf_abandon_operation(ctx, doc);
1128
0
    fz_rethrow(ctx);
1129
0
  }
1130
0
}
1131
1132
void pdf_js_event_init(pdf_js *js, pdf_obj *target, const char *value, int willCommit)
1133
0
{
1134
0
  if (js)
1135
0
  {
1136
0
    js_getglobal(js->imp, "event");
1137
0
    {
1138
0
      js_pushboolean(js->imp, 1);
1139
0
      js_setproperty(js->imp, -2, "rc");
1140
1141
0
      js_pushboolean(js->imp, willCommit);
1142
0
      js_setproperty(js->imp, -2, "willCommit");
1143
1144
0
      js_getregistry(js->imp, "Field");
1145
0
      js_newuserdata(js->imp, "Field", pdf_keep_obj(js->ctx, target), field_finalize);
1146
0
      js_setproperty(js->imp, -2, "target");
1147
1148
0
      js_pushstring(js->imp, value);
1149
0
      js_setproperty(js->imp, -2, "value");
1150
0
    }
1151
0
    js_pop(js->imp, 1);
1152
0
  }
1153
0
}
1154
1155
int pdf_js_event_result(pdf_js *js)
1156
0
{
1157
0
  int rc = 1;
1158
0
  if (js)
1159
0
  {
1160
0
    js_getglobal(js->imp, "event");
1161
0
    js_getproperty(js->imp, -1, "rc");
1162
0
    rc = js_tryboolean(js->imp, -1, 1);
1163
0
    js_pop(js->imp, 2);
1164
0
  }
1165
0
  return rc;
1166
0
}
1167
1168
int pdf_js_event_result_validate(pdf_js *js, char **newtext)
1169
0
{
1170
0
  int rc = 1;
1171
0
  *newtext = NULL;
1172
0
  if (js)
1173
0
  {
1174
0
    js_getglobal(js->imp, "event");
1175
0
    js_getproperty(js->imp, -1, "rc");
1176
0
    rc = js_tryboolean(js->imp, -1, 1);
1177
0
    js_pop(js->imp, 1);
1178
0
    if (rc)
1179
0
    {
1180
0
      js_getproperty(js->imp, -1, "value");
1181
0
      *newtext = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
1182
0
      js_pop(js->imp, 1);
1183
0
    }
1184
0
    js_pop(js->imp, 1);
1185
0
  }
1186
0
  return rc;
1187
0
}
1188
1189
void pdf_js_event_init_keystroke(pdf_js *js, pdf_obj *target, pdf_keystroke_event *evt)
1190
0
{
1191
0
  if (js)
1192
0
  {
1193
0
    pdf_js_event_init(js, target, evt->value, evt->willCommit);
1194
0
    js_getglobal(js->imp, "event");
1195
0
    {
1196
0
      js_pushstring(js->imp, evt->change);
1197
0
      js_setproperty(js->imp, -2, "change");
1198
0
      js_pushnumber(js->imp, evt->selStart);
1199
0
      js_setproperty(js->imp, -2, "selStart");
1200
0
      js_pushnumber(js->imp, evt->selEnd);
1201
0
      js_setproperty(js->imp, -2, "selEnd");
1202
0
    }
1203
0
    js_pop(js->imp, 1);
1204
0
  }
1205
0
}
1206
1207
int pdf_js_event_result_keystroke(pdf_js *js, pdf_keystroke_event *evt)
1208
0
{
1209
0
  int rc = 1;
1210
0
  if (js)
1211
0
  {
1212
0
    js_getglobal(js->imp, "event");
1213
0
    {
1214
0
      js_getproperty(js->imp, -1, "rc");
1215
0
      rc = js_tryboolean(js->imp, -1, 1);
1216
0
      js_pop(js->imp, 1);
1217
0
      if (rc)
1218
0
      {
1219
0
        js_getproperty(js->imp, -1, "change");
1220
0
        evt->newChange = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
1221
0
        js_pop(js->imp, 1);
1222
0
        js_getproperty(js->imp, -1, "value");
1223
0
        evt->newValue = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
1224
0
        js_pop(js->imp, 1);
1225
0
        js_getproperty(js->imp, -1, "selStart");
1226
0
        evt->selStart = js_tryinteger(js->imp, -1, 0);
1227
0
        js_pop(js->imp, 1);
1228
0
        js_getproperty(js->imp, -1, "selEnd");
1229
0
        evt->selEnd = js_tryinteger(js->imp, -1, 0);
1230
0
        js_pop(js->imp, 1);
1231
0
      }
1232
0
    }
1233
0
    js_pop(js->imp, 1);
1234
0
  }
1235
0
  return rc;
1236
0
}
1237
1238
char *pdf_js_event_value(pdf_js *js)
1239
0
{
1240
0
  char *value = NULL;
1241
0
  if (js)
1242
0
  {
1243
0
    js_getglobal(js->imp, "event");
1244
0
    js_getproperty(js->imp, -1, "value");
1245
0
    value = fz_strdup(js->ctx, js_trystring(js->imp, -1, "undefined"));
1246
0
    js_pop(js->imp, 2);
1247
0
  }
1248
0
  return value;
1249
0
}
1250
1251
void pdf_js_execute(pdf_js *js, const char *name, const char *source, char **result)
1252
0
{
1253
0
  fz_context *ctx;
1254
0
  js_State *J;
1255
1256
0
  if (!js)
1257
0
    return;
1258
1259
0
  ctx = js->ctx;
1260
0
  J = js->imp;
1261
1262
0
  pdf_begin_implicit_operation(ctx, js->doc);
1263
0
  fz_try(ctx)
1264
0
  {
1265
0
    if (js_ploadstring(J, name, source)) {
1266
0
      if (result)
1267
0
        *result = fz_strdup(ctx, js_trystring(J, -1, "Error"));
1268
0
      js_pop(J, 1);
1269
0
    } else {
1270
0
      js_pushundefined(J);
1271
0
      js_setlimit(J, PDF_JS_LIMIT_RUNTIME, PDF_JS_LIMIT_MEMORY);
1272
0
      if (js_pcall(J, 0)) {
1273
0
        if (result)
1274
0
          *result = fz_strdup(ctx, js_trystring(J, -1, "Error"));
1275
0
        else
1276
0
          fz_write_printf(ctx, fz_stddbg(ctx), "js: %s\n", js_trystring(J, -1, "Error"));
1277
0
        js_pop(J, 1);
1278
0
      } else {
1279
0
        if (result)
1280
0
          *result = fz_strdup(ctx, js_tryrepr(J, -1, "can't convert to string"));
1281
0
        js_pop(J, 1);
1282
0
      }
1283
0
    }
1284
0
    pdf_end_operation(ctx, js->doc);
1285
0
  }
1286
0
  fz_catch(ctx)
1287
0
  {
1288
0
    pdf_abandon_operation(ctx, js->doc);
1289
0
    fz_rethrow(ctx);
1290
0
  }
1291
0
}
1292
1293
pdf_js_console *pdf_js_get_console(fz_context *ctx, pdf_document *doc)
1294
0
{
1295
0
  return (doc && doc->js) ? doc->js->console : NULL;
1296
0
}
1297
1298
void pdf_js_set_console(fz_context *ctx, pdf_document *doc, pdf_js_console *console, void *user)
1299
0
{
1300
0
  if (doc->js)
1301
0
  {
1302
0
    if (doc->js->console && doc->js->console->drop)
1303
0
      doc->js->console->drop(doc->js->console, doc->js->console_user);
1304
1305
0
    doc->js->console = console;
1306
0
    doc->js->console_user = user;
1307
0
  }
1308
0
}
1309
1310
void pdf_enable_js(fz_context *ctx, pdf_document *doc)
1311
0
{
1312
0
  if (!doc->js)
1313
0
  {
1314
0
    doc->js = pdf_new_js(ctx, doc);
1315
0
    pdf_js_load_document_level(doc->js);
1316
0
  }
1317
0
}
1318
1319
void pdf_disable_js(fz_context *ctx, pdf_document *doc)
1320
0
{
1321
0
  pdf_drop_js(ctx, doc->js);
1322
0
  doc->js = NULL;
1323
0
}
1324
1325
int pdf_js_supported(fz_context *ctx, pdf_document *doc)
1326
0
{
1327
  return doc->js != NULL;
1328
0
}
1329
1330
#else /* FZ_ENABLE_JS */
1331
1332
void pdf_drop_js(fz_context *ctx, pdf_js *js) { }
1333
void pdf_enable_js(fz_context *ctx, pdf_document *doc) { }
1334
void pdf_disable_js(fz_context *ctx, pdf_document *doc) { }
1335
int pdf_js_supported(fz_context *ctx, pdf_document *doc) { return 0; }
1336
void pdf_js_event_init(pdf_js *js, pdf_obj *target, const char *value, int willCommit) { }
1337
void pdf_js_event_init_keystroke(pdf_js *js, pdf_obj *target, pdf_keystroke_event *evt) { }
1338
int pdf_js_event_result_keystroke(pdf_js *js, pdf_keystroke_event *evt) { return 1; }
1339
int pdf_js_event_result(pdf_js *js) { return 1; }
1340
char *pdf_js_event_value(pdf_js *js) { return ""; }
1341
void pdf_js_execute(pdf_js *js, const char *name, const char *source, char **result) { }
1342
int pdf_js_event_result_validate(pdf_js *js, char **newvalue) { *newvalue=NULL; return 1; }
1343
pdf_js_console *pdf_js_get_console(fz_context *ctx, pdf_document *doc) { return NULL; }
1344
void pdf_js_set_console(fz_context *ctx, pdf_document *doc, pdf_js_console *console, void *user) { }
1345
1346
1347
#endif /* FZ_ENABLE_JS */