Coverage Report

Created: 2025-12-03 07:00

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