Ticket #5477: 0001-Merge-NoScript-2.3.7-s-ChannelReplacement-stuff.patch

File 0001-Merge-NoScript-2.3.7-s-ChannelReplacement-stuff.patch, 15.8 KB (added by ma1, 7 years ago)

Patch: Merges NoScript 2.3.7's ChannelReplacement and seems to fix this issue

  • src/chrome/content/code/IOUtil.js

    From b66c3e8e666d25c917aa38b4dbc7da9a076b9803 Mon Sep 17 00:00:00 2001
    From: Giorgio Maone <g.maone@informaction.com>
    Date: Wed, 18 Apr 2012 00:58:32 +0200
    Subject: [PATCH 1/1] Merge NoScript 2.3.7's ChannelReplacement stuff
    
    
    Signed-off-by: Giorgio Maone <g.maone@informaction.com>
    ---
     src/chrome/content/code/IOUtil.js |  262 +++++++++++++++++++++----------------
     1 files changed, 147 insertions(+), 115 deletions(-)
    
    diff --git a/src/chrome/content/code/IOUtil.js b/src/chrome/content/code/IOUtil.js
    index 1ec1287..9ef4453 100644
    a b  
    1 //INCLUDE("DNS");
    2 
    31const IO = {
    42  readFile: function(file, charset) {
    53    var res;
    const IOUtil = { 
    260258  }
    261259 
    262260};
    263 
    264 function CtxCapturingListener(tracingChannel, onCapture) {
     261function CtxCapturingListener(tracingChannel, captureObserver) {
    265262  this.originalListener = tracingChannel.setNewListener(this);
    266   this.onCapture = onCapture;
     263  this.captureObserver = captureObserver;
    267264}
    268265CtxCapturingListener.prototype = {
    269266  originalListener: null,
    270267  originalCtx: null,
    271268  onStartRequest: function(request, ctx) {
    272269    this.originalCtx = ctx;
    273     if (this.onCapture) this.onCapture(request, ctx);
     270    if (this.captureObserver) {
     271      this.captureObserver.observeCapture(request, this);
     272    }
    274273  },
    275274  onDataAvailable: function(request, ctx, inputStream, offset, count) {},
    276275  onStopRequest: function(request, ctx, statusCode) {},
    277   QueryInterface: xpcom_generateQI([CI.nsIStreamListener])
     276  QueryInterface: xpcom_generateQI([Ci.nsIStreamListener])
    278277}
    279278
    280279function ChannelReplacement(chan, newURI, newMethod) {
    281280  return this._init(chan, newURI, newMethod);
    282281}
    283282
    284 ChannelReplacement.supported = "nsITraceableChannel" in CI;
     283ChannelReplacement.supported = "nsITraceableChannel" in Ci;
     284
     285ChannelReplacement.runWhenPending = function(channel, callback) {
     286  if (channel.isPending()) {
     287    callback();
     288    return false;
     289  } else {
     290    new LoadGroupWrapper(channel, callback);
     291    return true;
     292  }
     293};
     294
    285295
    286296ChannelReplacement.prototype = {
    287297  listener: null,
    ChannelReplacement.prototype = { 
    289299  oldChannel: null,
    290300  channel: null,
    291301  window: null,
    292 
     302  suspended: false,
     303 
    293304  get _unsupportedError() {
    294305    return new Error("Can't replace channels without nsITraceableChannel!");
    295306  },
    296307 
    297   get _mustClassify() {
    298     delete this.__proto__._mustClassify;
    299     return this.__proto__._mustClassify = !("LOAD_CLASSIFIER_URI" in CI.nsIChannel);
     308  get _classifierClass() {
     309    delete this.__proto__._classifierClass;
     310    return this.__proto__._classifierClass = Cc["@mozilla.org/channelclassifier"];
     311  },
     312 
     313  _autoHeadersRx: /^(?:Host|Cookie|Authorization)$|Cache|^If-/,
     314  visitHeader: function(key, val) {
     315    try {
     316      // we skip authorization and cache-related fields which should be automatically set
     317      if (!this._autoHeadersRx.test(key)) this.channel.setRequestHeader(key, val, false);
     318    } catch (e) {
     319      dump(e + "\n");
     320    }
    300321  },
    301322 
    302323  _init: function(chan, newURI, newMethod) {
    303     if (!(ChannelReplacement.supported && chan instanceof CI.nsITraceableChannel))
     324    if (!(ChannelReplacement.supported && chan instanceof Ci.nsITraceableChannel))
    304325      throw this._unsupportedError;
    305326 
    306327    newURI = newURI || chan.URI;
    307328   
    308329    var newChan = IOS.newChannelFromURI(newURI);
    309 
     330   
     331    this.oldChannel = chan;
     332    this.channel = newChan;
     333   
    310334    // porting of http://mxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/src/nsHttpChannel.cpp#2750
    311335   
    312336    var loadFlags = chan.loadFlags;
     337   
    313338    if (chan.URI.schemeIs("https"))
    314339      loadFlags &= ~chan.INHIBIT_PERSISTENT_CACHING;
    315340   
    316341   
    317342    newChan.loadGroup = chan.loadGroup;
    318343    newChan.notificationCallbacks = chan.notificationCallbacks;
    319     newChan.loadFlags = loadFlags;
     344    newChan.loadFlags = loadFlags | newChan.LOAD_REPLACE;
    320345   
    321     if (!(newChan instanceof CI.nsIHttpChannel))
    322       return newChan;
     346    if (!(newChan instanceof Ci.nsIHttpChannel))
     347      return this;
    323348   
    324349    // copy headers
    325     chan.visitRequestHeaders({
    326       visitHeader: function(key, val) {
    327         try {
    328          
    329           // we skip authorization and cache-related fields which should be automatically set
    330           if (/^(?:Host|Cookie|Authorization)$|Cache|^If-/.test(key)) return;
    331          
    332           newChan.setRequestHeader(key, val, false);
    333         } catch (e) {
    334           dump(e + "\n");
    335         }
    336       }
    337     });
    338    
    339    
     350    chan.visitRequestHeaders(this);
     351
    340352    if (!newMethod || newMethod === chan.requestMethod) {
    341       if (newChan instanceof CI.nsIUploadChannel && chan instanceof CI.nsIUploadChannel && chan.uploadStream ) {
     353      if (newChan instanceof Ci.nsIUploadChannel && chan instanceof Ci.nsIUploadChannel && chan.uploadStream ) {
    342354        var stream = chan.uploadStream;
    343         if (stream instanceof CI.nsISeekableStream) {
     355        if (stream instanceof Ci.nsISeekableStream) {
    344356          stream.seek(stream.NS_SEEK_SET, 0);
    345357        }
    346358       
    347359        try {
    348           var ctype = chan.getRequestHeader("Content-type");
    349           var clen = chan.getRequestHeader("Content-length");
     360          let ctype = chan.getRequestHeader("Content-type");
     361          let clen = chan.getRequestHeader("Content-length");
    350362          if (ctype && clen) {
    351363            newChan.setUploadStream(stream, ctype, parseInt(clen, 10));
    352364          }
    ChannelReplacement.prototype = { 
    363375    if (chan.referrer) newChan.referrer = chan.referrer;
    364376    newChan.allowPipelining = chan.allowPipelining;
    365377    newChan.redirectionLimit = chan.redirectionLimit - 1;
    366     if (chan instanceof CI.nsIHttpChannelInternal && newChan instanceof CI.nsIHttpChannelInternal) {
     378    if (chan instanceof Ci.nsIHttpChannelInternal && newChan instanceof Ci.nsIHttpChannelInternal) {
    367379      if (chan.URI == chan.documentURI) {
    368380        newChan.documentURI = newURI;
    369381      } else {
    ChannelReplacement.prototype = { 
    371383      }
    372384    }
    373385   
    374     if (chan instanceof CI.nsIEncodedChannel && newChan instanceof CI.nsIEncodedChannel) {
     386    if (chan instanceof Ci.nsIEncodedChannel && newChan instanceof Ci.nsIEncodedChannel) {
    375387      newChan.applyConversion = chan.applyConversion;
    376388    }
    377389   
    378390    // we can't transfer resume information because we can't access mStartPos and mEntityID :(
    379391    // http://mxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/src/nsHttpChannel.cpp#2826
    380392   
    381     if ("nsIApplicationCacheChannel" in CI &&
    382       chan instanceof CI.nsIApplicationCacheChannel && newChan instanceof CI.nsIApplicationCacheChannel) {
     393    if ("nsIApplicationCacheChannel" in Ci &&
     394      chan instanceof Ci.nsIApplicationCacheChannel && newChan instanceof Ci.nsIApplicationCacheChannel) {
    383395      newChan.applicationCache = chan.applicationCache;
    384396      newChan.inheritApplicationCache = chan.inheritApplicationCache;
    385397    }
    386398   
    387     if (chan instanceof CI.nsIPropertyBag && newChan instanceof CI.nsIWritablePropertyBag)
     399    if (chan instanceof Ci.nsIPropertyBag && newChan instanceof Ci.nsIWritablePropertyBag)
    388400      for (var properties = chan.enumerator, p; properties.hasMoreElements();)
    389         if ((p = properties.getNext()) instanceof CI.nsIProperty)
     401        if ((p = properties.getNext()) instanceof Ci.nsIProperty)
    390402          newChan.setProperty(p.name, p.value);
    391    
    392     this.oldChannel = chan;
    393     this.channel = newChan;
    394    
     403
    395404    if (chan.loadFlags & chan.LOAD_DOCUMENT_URI) {
    396405      this.window = IOUtil.findWindow(chan);
    397406    }
    ChannelReplacement.prototype = { 
    399408    return this;
    400409  },
    401410 
    402   _onChannelRedirect: function(trueRedir) {
     411  _onChannelRedirect: function() {
    403412    var oldChan = this.oldChannel;
    404413    var newChan = this.channel;
    405414   
    406     if (trueRedir) {
     415    if (this.realRedirect) {
    407416      if (oldChan.redirectionLimit === 0) {
    408417        oldChan.cancel(NS_ERROR_REDIRECT_LOOP);
    409418        throw NS_ERROR_REDIRECT_LOOP;
    410419      }
    411420    } else newChan.redirectionLimit += 1;
    412421   
    413     newChan.loadFlags |= newChan.LOAD_REPLACE;
     422   
    414423   
    415424    // nsHttpHandler::OnChannelRedirect()
    416425
    417     const CES = CI.nsIChannelEventSink;
     426    const CES = Ci.nsIChannelEventSink;
    418427    const flags = CES.REDIRECT_INTERNAL;
    419428    this._callSink(
    420       CC["@mozilla.org/netwerk/global-channel-event-sink;1"].getService(CES),
     429      Cc["@mozilla.org/netwerk/global-channel-event-sink;1"].getService(CES),
    421430      oldChan, newChan, flags);
    422431    var sink;
    423432   
    424     for (let cess = CC['@mozilla.org/categorymanager;1']
    425               .getService(CI.nsICategoryManager)
    426               .enumerateCategory("net-channel-event-sinks");
     433    for (let cess = ns.categoryManager.enumerateCategory("net-channel-event-sinks");
    427434          cess.hasMoreElements();
    428435        ) {
    429436      sink = cess.getNext();
    ChannelReplacement.prototype = { 
    437444   
    438445    newChan.originalURI = oldChan.originalURI;
    439446   
    440     sink = IOUtil.queryNotificationCallbacks(oldChan, CI.nsIHttpEventSink);
     447    sink = IOUtil.queryNotificationCallbacks(oldChan, Ci.nsIHttpEventSink);
    441448    if (sink) sink.onRedirect(oldChan, newChan);
    442449  },
    443450 
    ChannelReplacement.prototype = { 
    460467    }
    461468  },
    462469 
    463   get _redirectCallback() {
    464     delete this.__proto__._redirectCallback;
    465     return this.__proto__._redirectCallback = ("nsIAsyncVerifyRedirectCallback" in CI)
     470  _redirectCallback: ("nsIAsyncVerifyRedirectCallback" in Ci)
    466471    ? {
    467         QueryInterface: xpcom_generateQI([CI.nsIAsyncVerifyRedirectCallback]),
     472        QueryInterface: xpcom_generateQI([Ci.nsIAsyncVerifyRedirectCallback]),
    468473        onRedirectVerifyCallback: function(result) {}
    469474      }
    470     : null;
    471   },
     475    : null
     476  ,
    472477 
    473   replace: function(isRedir, callback) {
     478  replace: function(realRedirect, callback) {
    474479    let self = this;
    475480    let oldChan = this.oldChannel;
    476     this.isRedir = !!isRedir;
     481    this.realRedirect = !!realRedirect;
    477482    if (typeof(callback) !== "function") {
    478483      callback = this._defaultCallback;
    479484    }
    480     IOUtil.runWhenPending(oldChan, function() {
     485    ChannelReplacement.runWhenPending(oldChan, function() {
    481486      if (oldChan.status) return; // channel's doom had been already defined
    482      
    483       let ccl = new CtxCapturingListener(oldChan,
    484         function() {
    485           try {
    486             callback(self._replaceNow(isRedir, this))
    487           } catch (e) {
    488             self.dispose();
    489           }
    490         });
     487     
     488      let ccl = new CtxCapturingListener(oldChan, self);
    491489      self.loadGroup = oldChan.loadGroup;
     490     
    492491      oldChan.loadGroup = null; // prevents the wheel from stopping spinning
    493       // this calls asyncAbort, which calls onStartRequest on our listener
    494       oldChan.cancel(NS_BINDING_REDIRECTED);
     492     
     493   
     494      if (self._redirectCallback) { // Gecko >= 2
     495        // this calls asyncAbort, which calls onStartRequest on our listener
     496        oldChan.cancel(NS_BINDING_REDIRECTED);
     497        self.suspend(); // believe it or not, this will defer asyncAbort() notifications until resume()
     498        callback(self);
     499      } else {
     500        // legacy (Gecko < 2)
     501        self.observeCapture = function(req, ccl) {
     502          self.open = function() { self._redirect(ccl) }
     503          callback(self);
     504        }
     505        oldChan.cancel(NS_BINDING_REDIRECTED);
     506      }
     507     
     508
    495509    });
    496510  },
    497511 
    498   _defaultCallback: function(replacement) {
    499     replacement.open();
     512  observeCapture: function(req, ccl) {
     513    this._redirect(ccl);
    500514  },
    501515 
    502   _replaceNow: function(isRedir, ccl) {
    503     let oldChan = this.oldChannel;
    504     oldChan.loadGroup = this.loadGroup;
    505    
    506     this._onChannelRedirect(isRedir);
    507    
    508     // dirty trick to grab listenerContext
    509    
    510     this.listener = ccl.originalListener;
    511     this.context = ccl.originalCtx;
    512     return this;
     516  _defaultCallback: function(replacement) {
     517    replacement.open();
    513518  },
    514  
     519
    515520  open: function() {
     521    this.resume(); // this triggers asyncAbort and the listeners in cascade
     522  },
     523  _redirect: function(ccl) {
    516524    let oldChan = this.oldChannel,
    517525      newChan = this.channel,
    518526      overlap;
    519    
    520     /* XXX: Hack
     527
    521528    if (!(this.window && (overlap = ABERequest.getLoadingChannel(this.window)) !== oldChan)) {
    522529      try {
    523530        if (ABE.consoleDump && this.window) {
    524531          ABE.log("Opening delayed channel: " + oldChan.name + " - (current loading channel for this window " + (overlap && overlap.name) + ")");
    525532        }
    526     */
    527     try {
    528       newChan.asyncOpen(this.listener, this.context);
    529      
    530       // safe browsing hook
    531       if (this._mustClassify)
    532         CC["@mozilla.org/channelclassifier"].createInstance(CI.nsIChannelClassifier).start(newChan, true);
    533      
    534     } catch (e) {}
    535 
     533       
     534         oldChan.loadGroup = this.loadGroup;
     535   
     536        this._onChannelRedirect();
     537        newChan.asyncOpen(ccl.originalListener, ccl.originalCtx);
     538       
     539        if (this.window && this.window != IOUtil.findWindow(newChan)) {
     540          // late diverted load, unwanted artifact, abort
     541          IOUtil.abort(newChan);
     542        } else {
     543          // safe browsing hook
     544          if (this._classifierClass)
     545            this._classifierClass.createInstance(Ci.nsIChannelClassifier).start(newChan, true);
     546        }
     547      } catch (e) {
     548        ABE.log(e);
     549      }
     550    } else {
     551      if (ABE.consoleDump) {
     552        ABE.log("Detected double load on the same window: " + oldChan.name + " - " + (overlap && overlap.name));
     553      }
     554    }
    536555   
    537556    this.dispose();
    538557  },
    539558 
     559  suspend: function() {
     560    if (!this.suspended) {
     561      this.oldChannel.suspend();
     562      this.suspended = true;
     563    }
     564  },
     565  resume: function() {
     566    if (this.suspended) {
     567      this.suspended = false;
     568      try {
     569        this.oldChannel.resume();
     570      } catch (e) {}
     571    }
     572  },
     573 
    540574  dispose: function() {
     575    this.resume();
    541576    if (this.loadGroup) {
    542577      try {
    543578        this.loadGroup.removeRequest(this.oldChannel, null, NS_BINDING_REDIRECTED);
    ChannelReplacement.prototype = { 
    546581    }
    547582
    548583  }
    549 }
     584};
    550585
    551 function LoadGroupWrapper(channel, callbacks) {
     586function LoadGroupWrapper(channel, callback) {
    552587  this._channel = channel;
    553588  this._inner = channel.loadGroup;
    554   this._callbacks = callbacks;
     589  this._callback = callback;
    555590  channel.loadGroup = this;
    556591}
    557592LoadGroupWrapper.prototype = {
    558   QueryInterface: xpcom_generateQI([CI.nsILoadGroup]),
     593  QueryInterface: xpcom_generateQI([Ci.nsILoadGroup]),
    559594 
    560595  get activeCount() {
    561596    return this._inner ? this._inner.activeCount : 0;
    LoadGroupWrapper.prototype = { 
    589624    } catch(e) {
    590625      // addRequest may have not been implemented
    591626    }
    592     if (r === this._channel && ("addRequest" in this._callbacks))
     627    if (r === this._channel)
    593628      try {
    594         this._callbacks.addRequest(r, ctx);
     629        this._callback(r, ctx);
    595630      } catch (e) {}
    596631  },
    597632  removeRequest: function(r, ctx, status) {
    598633    this.detach();
    599634    if (this._inner) this._inner.removeRequest(r, ctx, status);
    600     if (r === this._channel && ("removeRequest" in this._callbacks))
    601       try {
    602         this._callbacks.removeRequest(r, ctx, status);
    603       } catch (e) {}
    604635  },
    605636 
    606637  detach: function() {
    607638    if (this._channel.loadGroup) this._channel.loadGroup = this._inner;
    608639  },
    609640  _emptyEnum: {
    610     QueryInterface: xpcom_generateQI([CI.nsISimpleEnumerator]),
    611     getNext: function() { return null; },
    612     hasMoreElements: function() { return false; }
     641    QueryInterface: xpcom_generateQI([Ci.nsISimpleEnumerator]),
     642    getNext: function() null,
     643    hasMoreElements: function() false
    613644  }
    614 }
     645};
     646