@@ -14,6 +14,7 @@ const MOZ_CENTRAL = PDFJSSCRIPT_MOZ_CENTRAL;
14
14
const PDFJS_EVENT_ID = 'pdf.js.message' ;
15
15
const PDF_CONTENT_TYPE = 'application/pdf' ;
16
16
const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX' ;
17
+ const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html' ;
17
18
const MAX_DATABASE_LENGTH = 4096 ;
18
19
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' ;
19
20
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}' ;
@@ -122,9 +123,68 @@ function getLocalizedString(strings, id, property) {
122
123
return id ;
123
124
}
124
125
126
+ // PDF data storage
127
+ function PdfDataListener (length ) {
128
+ this .length = length ; // less than 0, if length is unknown
129
+ this .data = new Uint8Array (length >= 0 ? length : 0x10000 );
130
+ this .loaded = 0 ;
131
+ }
132
+
133
+ PdfDataListener .prototype = {
134
+ set : function PdfDataListener_set (chunk , offset ) {
135
+ if (this .length < 0 ) {
136
+ var willBeLoaded = this .loaded + chunk .length ;
137
+ // data length is unknown and new chunk will not fit in the existing
138
+ // buffer, resizing the buffer by doubling the its last length
139
+ if (this .data .length < willBeLoaded ) {
140
+ var newLength = this .data .length ;
141
+ for (; newLength < willBeLoaded ; newLength *= 2 ) {}
142
+ var newData = new Uint8Array (newLength );
143
+ newData .set (this .data );
144
+ this .data = newData ;
145
+ }
146
+ this .data .set (chunk , this .loaded );
147
+ this .loaded = willBeLoaded ;
148
+ } else {
149
+ this .data .set (chunk , offset );
150
+ this .loaded = offset + chunk .length ;
151
+ this .onprogress (this .loaded , this .length );
152
+ }
153
+ },
154
+ getData : function PdfDataListener_getData () {
155
+ var data = this .length >= 0 ? this .data :
156
+ this .data .subarray (0 , this .loaded );
157
+ delete this .data ; // releasing temporary storage
158
+ return data ;
159
+ },
160
+ finish : function PdfDataListener_finish () {
161
+ this .isDataReady = true ;
162
+ if (this .oncompleteCallback ) {
163
+ this .oncompleteCallback (this .getData ());
164
+ }
165
+ },
166
+ error : function PdfDataListener_error (errorCode ) {
167
+ this .errorCode = errorCode ;
168
+ if (this .oncompleteCallback ) {
169
+ this .oncompleteCallback (null , errorCode );
170
+ }
171
+ },
172
+ onprogress : function () {},
173
+ set oncomplete (value ) {
174
+ this .oncompleteCallback = value ;
175
+ if (this .isDataReady ) {
176
+ value (this .getData ());
177
+ }
178
+ if (this .errorCode ) {
179
+ value (null , this .errorCode );
180
+ }
181
+ }
182
+ };
183
+
125
184
// All the priviledged actions.
126
- function ChromeActions (domWindow ) {
185
+ function ChromeActions (domWindow , dataListener ) {
127
186
this .domWindow = domWindow ;
187
+ this .dataListener = dataListener ;
128
188
}
129
189
130
190
ChromeActions .prototype = {
@@ -194,6 +254,38 @@ ChromeActions.prototype = {
194
254
getLocale : function () {
195
255
return getStringPref ('general.useragent.locale' , 'en-US' );
196
256
},
257
+ getLoadingType : function () {
258
+ return this .dataListener ? 'passive' : 'active' ;
259
+ },
260
+ initPassiveLoading : function () {
261
+ if (!this .dataListener )
262
+ return false ;
263
+
264
+ var domWindow = this .domWindow ;
265
+ this .dataListener .onprogress =
266
+ function ChromeActions_dataListenerProgress (loaded , total ) {
267
+
268
+ domWindow .postMessage ({
269
+ pdfjsLoadAction : 'progress' ,
270
+ loaded : loaded ,
271
+ total : total
272
+ }, '*' );
273
+ };
274
+
275
+ this .dataListener .oncomplete =
276
+ function ChromeActions_dataListenerComplete (data , errorCode ) {
277
+
278
+ domWindow .postMessage ({
279
+ pdfjsLoadAction : 'complete' ,
280
+ data : data ,
281
+ errorCode : errorCode
282
+ }, '*' );
283
+
284
+ delete this .dataListener ;
285
+ };
286
+
287
+ return true ;
288
+ },
197
289
getStrings : function (data ) {
198
290
try {
199
291
// Lazy initialization of localizedStrings
@@ -341,42 +433,64 @@ PdfStreamConverter.prototype = {
341
433
asyncConvertData : function (aFromType , aToType , aListener , aCtxt ) {
342
434
if (!isEnabled ())
343
435
throw Cr .NS_ERROR_NOT_IMPLEMENTED ;
344
- // Ignoring HTTP POST requests -- pdf.js has to repeat the request.
345
- var skipConversion = false ;
346
- try {
347
- var request = aCtxt ;
348
- request .QueryInterface (Ci .nsIHttpChannel );
349
- skipConversion = (request .requestMethod !== 'GET' );
350
- } catch (e ) {
351
- // Non-HTTP request... continue normally.
436
+
437
+ var useFetchByChrome = getBoolPref (PREF_PREFIX + '.fetchByChrome' , true );
438
+ if (!useFetchByChrome ) {
439
+ // Ignoring HTTP POST requests -- pdf.js has to repeat the request.
440
+ var skipConversion = false ;
441
+ try {
442
+ var request = aCtxt ;
443
+ request .QueryInterface (Ci .nsIHttpChannel );
444
+ skipConversion = (request .requestMethod !== 'GET' );
445
+ } catch (e ) {
446
+ // Non-HTTP request... continue normally.
447
+ }
448
+ if (skipConversion )
449
+ throw Cr .NS_ERROR_NOT_IMPLEMENTED ;
352
450
}
353
- if (skipConversion )
354
- throw Cr .NS_ERROR_NOT_IMPLEMENTED ;
355
451
356
452
// Store the listener passed to us
357
453
this .listener = aListener ;
358
454
},
359
455
360
456
// nsIStreamListener::onDataAvailable
361
457
onDataAvailable : function (aRequest , aContext , aInputStream , aOffset , aCount ) {
362
- // Do nothing since all the data loading is handled by the viewer.
363
- log ('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!' );
458
+ if (!this .dataListener ) {
459
+ // Do nothing since all the data loading is handled by the viewer.
460
+ return ;
461
+ }
462
+
463
+ var binaryStream = this .binaryStream ;
464
+ binaryStream .setInputStream (aInputStream );
465
+ this .dataListener .set (binaryStream .readByteArray (aCount ), aOffset );
364
466
},
365
467
366
468
// nsIRequestObserver::onStartRequest
367
469
onStartRequest : function (aRequest , aContext ) {
368
470
369
471
// Setup the request so we can use it below.
370
472
aRequest .QueryInterface (Ci .nsIChannel );
371
- // Cancel the request so the viewer can handle it.
372
- aRequest .cancel (Cr .NS_BINDING_ABORTED );
473
+ var useFetchByChrome = getBoolPref (PREF_PREFIX + '.fetchByChrome' , true );
474
+ var dataListener ;
475
+ if (useFetchByChrome ) {
476
+ // Creating storage for PDF data
477
+ var contentLength = aRequest .contentLength ;
478
+ dataListener = new PdfDataListener (contentLength );
479
+ this .dataListener = dataListener ;
480
+ this .binaryStream = Cc ['@mozilla.org/binaryinputstream;1' ]
481
+ .createInstance (Ci .nsIBinaryInputStream );
482
+ } else {
483
+ // Cancel the request so the viewer can handle it.
484
+ aRequest .cancel (Cr .NS_BINDING_ABORTED );
485
+ }
373
486
374
487
// Create a new channel that is viewer loaded as a resource.
375
488
var ioService = Services .io ;
376
489
var channel = ioService .newChannel (
377
- 'resource://pdf.js/web/viewer.html' , null , null );
490
+ PDF_VIEWER_WEB_PAGE , null , null );
378
491
379
492
var listener = this .listener ;
493
+ var self = this ;
380
494
// Proxy all the request observer calls, when it gets to onStopRequest
381
495
// we can get the dom window.
382
496
var proxy = {
@@ -390,8 +504,8 @@ PdfStreamConverter.prototype = {
390
504
var domWindow = getDOMWindow (channel );
391
505
// Double check the url is still the correct one.
392
506
if (domWindow .document .documentURIObject .equals (aRequest .URI )) {
393
- let requestListener = new RequestListener (
394
- new ChromeActions ( domWindow ) );
507
+ let actions = new ChromeActions ( domWindow , dataListener );
508
+ let requestListener = new RequestListener ( actions );
395
509
domWindow .addEventListener (PDFJS_EVENT_ID , function (event ) {
396
510
requestListener .receive (event );
397
511
}, false , true );
@@ -403,11 +517,33 @@ PdfStreamConverter.prototype = {
403
517
// Keep the URL the same so the browser sees it as the same.
404
518
channel .originalURI = aRequest .URI ;
405
519
channel .asyncOpen (proxy , aContext );
520
+ if (useFetchByChrome ) {
521
+ // We can use resource principal when data is fetched by the chrome
522
+ // e.g. useful for NoScript
523
+ var securityManager = Cc ['@mozilla.org/scriptsecuritymanager;1' ]
524
+ .getService (Ci .nsIScriptSecurityManager );
525
+ var uri = ioService .newURI (PDF_VIEWER_WEB_PAGE , null , null );
526
+ // FF16 and below had getCodebasePrincipal (bug 774585)
527
+ var resourcePrincipal = 'getSimpleCodebasePrincipal' in securityManager ?
528
+ securityManager .getSimpleCodebasePrincipal (uri ) :
529
+ securityManager .getCodebasePrincipal (uri );
530
+ channel .owner = resourcePrincipal ;
531
+ }
406
532
},
407
533
408
534
// nsIRequestObserver::onStopRequest
409
535
onStopRequest : function (aRequest , aContext , aStatusCode ) {
410
- // Do nothing.
536
+ if (!this .dataListener ) {
537
+ // Do nothing
538
+ return ;
539
+ }
540
+
541
+ if (Components .isSuccessCode (aStatusCode ))
542
+ this .dataListener .finish ();
543
+ else
544
+ this .dataListener .error (aStatusCode );
545
+ delete this .dataListener ;
546
+ delete this .binaryStream ;
411
547
}
412
548
};
413
549
0 commit comments