merge inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Mon, 02 Jun 2014 18:37:29 -0700
changeset 205416 afaeec863f74759a837d769beca7a8d0c4399f98
parent 205410 99137ca577706fe4563d9048641917654cd7ddf9 (current diff)
parent 205415 d74a2f6f81eea17282835eb8d8a85ce10c2fa0d6 (diff)
child 205423 10c7869590b13c932d1af4b013aee2e1c02c7118
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge inbound to m-c
--- a/browser/base/content/test/general/browser_devices_get_user_media.js
+++ b/browser/base/content/test/general/browser_devices_get_user_media.js
@@ -55,17 +55,17 @@ function expectObserverCalled(aTopic) {
     --gObservedTopics[aTopic];
 }
 
 function expectNoObserverCalled() {
   for (let topic in gObservedTopics) {
     if (gObservedTopics[topic])
       is(gObservedTopics[topic], 0, topic + " notification unexpected");
   }
-  gObservedTopics = {}
+  gObservedTopics = {};
 }
 
 function promiseMessage(aMessage, aAction) {
   let deferred = Promise.defer();
 
   content.addEventListener("message", function messageListener(event) {
     content.removeEventListener("message", messageListener);
     is(event.data, aMessage, "received " + aMessage);
@@ -811,17 +811,17 @@ function test() {
         // Cleanup before the next test
         expectNoObserverCalled();
       }
     }).then(finish, ex => {
      ok(false, "Unexpected Exception: " + ex);
      finish();
     });
   }, true);
-  let rootDir = getRootDirectory(gTestPath)
+  let rootDir = getRootDirectory(gTestPath);
   rootDir = rootDir.replace("chrome://mochitests/content/",
                             "https://example.com/");
   content.location = rootDir + "get_user_media.html";
 }
 
 
 function wait(time) {
   let deferred = Promise.defer();
--- a/browser/base/content/test/general/get_user_media.html
+++ b/browser/base/content/test/general/get_user_media.html
@@ -1,24 +1,40 @@
 <!DOCTYPE html>
 <html>
 <head><meta charset="UTF-8"></head>
 <body>
 <div id="message"></div>
 <script>
+// Specifies whether we are using fake streams to run this automation
+var useFakeStreams = true;
+try {
+  var audioDevice = SpecialPowers.getCharPref("media.audio_loopback_dev");
+  var videoDevice = SpecialPowers.getCharPref("media.video_loopback_dev");
+  dump("TEST DEVICES: Using media devices:\n");
+  dump("audio: " + audioDevice + "\nvideo: " + videoDevice + "\n");
+  useFakeStreams = false;
+} catch (e) {
+  dump("TEST DEVICES: No test devices found (in media.{audio,video}_loopback_dev, using fake streams.\n");
+  useFakeStreams = true;
+}
+
 function message(m) {
   document.getElementById("message").innerHTML = m;
   window.parent.postMessage(m, "*");
 }
 
 var gStream;
 
 function requestDevice(aAudio, aVideo) {
-  window.navigator.mozGetUserMedia({video: aVideo, audio: aAudio, fake: true},
-                                   function(stream) {
+  var opts = {video: aVideo, audio: aAudio};
+  if (useFakeStreams) {
+    opts.fake = true;
+  }
+  window.navigator.mozGetUserMedia(opts, function(stream) {
     gStream = stream;
     message("ok");
   }, function(err) { message("error: " + err); });
 }
 message("pending");
 
 function closeStream() {
   if (!gStream)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1018621.js
@@ -0,0 +1,7 @@
+function strictSome(k) {
+  "use strict";
+  for (var i = 0; i < args.length; i++)
+    assertEq(arguments[i], args[i], "wrong argument " + i);
+}
+args = [8, 6, 7, NaN, undefined, 0.3];
+strictSome.call(NaN, 8, 6, 7, NaN, undefined, 0.3);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -420,18 +420,21 @@ class CompileInfo
         // preserve the scope chain, because it may be needed to construct the
         // arguments object during bailout. If we've already created an
         // arguments object (or got one via OSR), preserve that as well.
         if (hasArguments() && (slot == scopeChainSlot() || slot == argsObjSlot()))
             return true;
 
         // Function.arguments can be used to access all arguments in non-strict
         // scripts, so we can't optimize out any arguments.
-        if (!script()->strict() && firstArgSlot() <= slot && slot - firstArgSlot() < nargs())
+        if ((hasArguments() || !script()->strict()) &&
+            firstArgSlot() <= slot && slot - firstArgSlot() < nargs())
+        {
             return true;
+        }
 
         return false;
     }
 
   private:
     unsigned nimplicit_;
     unsigned nargs_;
     unsigned nfixedvars_;
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1651,35 +1651,74 @@ JitFrameIterator::safepoint() const
 
 const OsiIndex *
 JitFrameIterator::osiIndex() const
 {
     SafepointReader reader(ionScript(), safepoint());
     return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
 }
 
-template <AllowGC allowGC>
+InlineFrameIterator::InlineFrameIterator(ThreadSafeContext *cx, const JitFrameIterator *iter)
+  : callee_(cx),
+    script_(cx)
+{
+    resetOn(iter);
+}
+
+InlineFrameIterator::InlineFrameIterator(JSRuntime *rt, const JitFrameIterator *iter)
+  : callee_(rt),
+    script_(rt)
+{
+    resetOn(iter);
+}
+
+InlineFrameIterator::InlineFrameIterator(ThreadSafeContext *cx, const IonBailoutIterator *iter)
+  : frame_(iter),
+    framesRead_(0),
+    frameCount_(UINT32_MAX),
+    callee_(cx),
+    script_(cx)
+{
+    if (iter) {
+        start_ = SnapshotIterator(*iter);
+        findNextFrame();
+    }
+}
+
+InlineFrameIterator::InlineFrameIterator(ThreadSafeContext *cx, const InlineFrameIterator *iter)
+  : frame_(iter ? iter->frame_ : nullptr),
+    framesRead_(0),
+    frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
+    callee_(cx),
+    script_(cx)
+{
+    if (frame_) {
+        start_ = SnapshotIterator(*frame_);
+        // findNextFrame will iterate to the next frame and init. everything.
+        // Therefore to settle on the same frame, we report one frame less readed.
+        framesRead_ = iter->framesRead_ - 1;
+        findNextFrame();
+    }
+}
+
 void
-InlineFrameIteratorMaybeGC<allowGC>::resetOn(const JitFrameIterator *iter)
+InlineFrameIterator::resetOn(const JitFrameIterator *iter)
 {
     frame_ = iter;
     framesRead_ = 0;
     frameCount_ = UINT32_MAX;
 
     if (iter) {
         start_ = SnapshotIterator(*iter);
         findNextFrame();
     }
 }
-template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const JitFrameIterator *iter);
-template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const JitFrameIterator *iter);
 
-template <AllowGC allowGC>
 void
-InlineFrameIteratorMaybeGC<allowGC>::findNextFrame()
+InlineFrameIterator::findNextFrame()
 {
     JS_ASSERT(more());
 
     si_ = start_;
 
     // Read the initial frame out of the C stack.
     callee_ = frame_->maybeCallee();
     script_ = frame_->script();
@@ -1751,65 +1790,75 @@ InlineFrameIteratorMaybeGC<allowGC>::fin
     // iteration that we have done.
     if (frameCount_ == UINT32_MAX) {
         MOZ_ASSERT(!si_.moreFrames());
         frameCount_ = i;
     }
 
     framesRead_++;
 }
-template void InlineFrameIteratorMaybeGC<NoGC>::findNextFrame();
-template void InlineFrameIteratorMaybeGC<CanGC>::findNextFrame();
+
+JSObject *
+InlineFrameIterator::computeScopeChain(Value scopeChainValue) const
+{
+    if (scopeChainValue.isObject())
+        return &scopeChainValue.toObject();
 
-template <AllowGC allowGC>
+    if (isFunctionFrame()) {
+        // Heavyweight functions should always have a scope chain.
+        MOZ_ASSERT(!callee()->isHeavyweight());
+        return callee()->environment();
+    }
+
+    // Ion does not handle scripts that are not compile-and-go.
+    MOZ_ASSERT(!script()->isForEval());
+    MOZ_ASSERT(script()->compileAndGo());
+    return &script()->global();
+}
+
 bool
-InlineFrameIteratorMaybeGC<allowGC>::isFunctionFrame() const
+InlineFrameIterator::isFunctionFrame() const
 {
     return !!callee_;
 }
-template bool InlineFrameIteratorMaybeGC<NoGC>::isFunctionFrame() const;
-template bool InlineFrameIteratorMaybeGC<CanGC>::isFunctionFrame() const;
 
 MachineState
 MachineState::FromBailout(mozilla::Array<uintptr_t, Registers::Total> &regs,
                           mozilla::Array<double, FloatRegisters::Total> &fpregs)
 {
     MachineState machine;
 
     for (unsigned i = 0; i < Registers::Total; i++)
         machine.setRegisterLocation(Register::FromCode(i), &regs[i]);
     for (unsigned i = 0; i < FloatRegisters::Total; i++)
         machine.setRegisterLocation(FloatRegister::FromCode(i), &fpregs[i]);
 
     return machine;
 }
 
-template <AllowGC allowGC>
 bool
-InlineFrameIteratorMaybeGC<allowGC>::isConstructing() const
+InlineFrameIterator::isConstructing() const
 {
     // Skip the current frame and look at the caller's.
     if (more()) {
-        InlineFrameIteratorMaybeGC<allowGC> parent(GetJSContextFromJitCode(), this);
+        InlineFrameIterator parent(GetJSContextFromJitCode(), this);
         ++parent;
 
         // Inlined Getters and Setters are never constructing.
         if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc()))
             return false;
 
         // In the case of a JS frame, look up the pc from the snapshot.
         JS_ASSERT(IsCallPC(parent.pc()));
 
         return (JSOp)*parent.pc() == JSOP_NEW;
     }
 
     return frame_->isConstructing();
 }
-template bool InlineFrameIteratorMaybeGC<NoGC>::isConstructing() const;
-template bool InlineFrameIteratorMaybeGC<CanGC>::isConstructing() const;
 
 bool
 JitFrameIterator::isConstructing() const
 {
     JitFrameIterator parent(*this);
 
     // Skip the current frame and look at the caller's.
     do {
@@ -1916,19 +1965,18 @@ JitFrameIterator::dumpBaseline() const
         Value *v = frame->valueSlot(i);
         js_DumpValue(*v);
 #else
         fprintf(stderr, "?\n");
 #endif
     }
 }
 
-template <AllowGC allowGC>
 void
-InlineFrameIteratorMaybeGC<allowGC>::dump() const
+InlineFrameIterator::dump() const
 {
     if (more())
         fprintf(stderr, " JS frame (inlined)\n");
     else
         fprintf(stderr, " JS frame\n");
 
     bool isFunction = false;
     if (isFunctionFrame()) {
@@ -1977,18 +2025,16 @@ InlineFrameIteratorMaybeGC<allowGC>::dum
         js_DumpValue(si.maybeRead());
 #else
         fprintf(stderr, "?\n");
 #endif
     }
 
     fputc('\n', stderr);
 }
-template void InlineFrameIteratorMaybeGC<NoGC>::dump() const;
-template void InlineFrameIteratorMaybeGC<CanGC>::dump() const;
 
 void
 JitFrameIterator::dump() const
 {
     switch (type_) {
       case JitFrame_Entry:
         fprintf(stderr, " Entry frame\n");
         fprintf(stderr, "  Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
--- a/js/src/jit/JitFrameIterator-inl.h
+++ b/js/src/jit/JitFrameIterator-inl.h
@@ -13,32 +13,16 @@
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/IonFrames.h"
 
 namespace js {
 namespace jit {
 
-template <AllowGC allowGC>
-inline
-InlineFrameIteratorMaybeGC<allowGC>::InlineFrameIteratorMaybeGC(ThreadSafeContext *cx,
-                                                                const IonBailoutIterator *iter)
-  : frame_(iter),
-    framesRead_(0),
-    frameCount_(UINT32_MAX),
-    callee_(cx),
-    script_(cx)
-{
-    if (iter) {
-        start_ = SnapshotIterator(*iter);
-        findNextFrame();
-    }
-}
-
 inline BaselineFrame *
 JitFrameIterator::baselineFrame() const
 {
     JS_ASSERT(isBaselineJS());
     return (BaselineFrame *)(fp() - BaselineFrame::FramePointerOffset - BaselineFrame::Size());
 }
 
 template <typename T>
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -448,90 +448,47 @@ class SnapshotIterator
     void spewBailingFrom() const {
         snapshot_.spewBailingFrom();
     }
 #endif
 };
 
 // Reads frame information in callstack order (that is, innermost frame to
 // outermost frame).
-template <AllowGC allowGC=CanGC>
-class InlineFrameIteratorMaybeGC
+class InlineFrameIterator
 {
     const JitFrameIterator *frame_;
     SnapshotIterator start_;
     SnapshotIterator si_;
     uint32_t framesRead_;
 
     // When the inline-frame-iterator is created, this variable is defined to
     // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
     // the innermost frame, is used to update this counter to the number of
     // frames contained in the recover buffer.
     uint32_t frameCount_;
 
-    typename MaybeRooted<JSFunction*, allowGC>::RootType callee_;
-    typename MaybeRooted<JSScript*, allowGC>::RootType script_;
+    RootedFunction callee_;
+    RootedScript script_;
     jsbytecode *pc_;
     uint32_t numActualArgs_;
 
     struct Nop {
         void operator()(const Value &v) { }
     };
 
   private:
     void findNextFrame();
-
-    JSObject *computeScopeChain(Value scopeChainValue) const {
-        if (scopeChainValue.isObject())
-            return &scopeChainValue.toObject();
-
-        if (isFunctionFrame()) {
-            // Heavyweight functions should always have a scope chain.
-            MOZ_ASSERT(!callee()->isHeavyweight());
-            return callee()->environment();
-        }
-
-        // Ion does not handle scripts that are not compile-and-go.
-        MOZ_ASSERT(!script()->isForEval());
-        MOZ_ASSERT(script()->compileAndGo());
-        return &script()->global();
-    }
+    JSObject *computeScopeChain(Value scopeChainValue) const;
 
   public:
-    InlineFrameIteratorMaybeGC(ThreadSafeContext *cx, const JitFrameIterator *iter)
-      : callee_(cx),
-        script_(cx)
-    {
-        resetOn(iter);
-    }
-
-    InlineFrameIteratorMaybeGC(JSRuntime *rt, const JitFrameIterator *iter)
-      : callee_(rt),
-        script_(rt)
-    {
-        resetOn(iter);
-    }
-
-    InlineFrameIteratorMaybeGC(ThreadSafeContext *cx, const IonBailoutIterator *iter);
-
-    InlineFrameIteratorMaybeGC(ThreadSafeContext *cx, const InlineFrameIteratorMaybeGC *iter)
-      : frame_(iter ? iter->frame_ : nullptr),
-        framesRead_(0),
-        frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
-        callee_(cx),
-        script_(cx)
-    {
-        if (frame_) {
-            start_ = SnapshotIterator(*frame_);
-            // findNextFrame will iterate to the next frame and init. everything.
-            // Therefore to settle on the same frame, we report one frame less readed.
-            framesRead_ = iter->framesRead_ - 1;
-            findNextFrame();
-        }
-    }
+    InlineFrameIterator(ThreadSafeContext *cx, const JitFrameIterator *iter);
+    InlineFrameIterator(JSRuntime *rt, const JitFrameIterator *iter);
+    InlineFrameIterator(ThreadSafeContext *cx, const IonBailoutIterator *iter);
+    InlineFrameIterator(ThreadSafeContext *cx, const InlineFrameIterator *iter);
 
     bool more() const {
         return frame_ && framesRead_ < frameCount_;
     }
     JSFunction *callee() const {
         JS_ASSERT(callee_);
         return callee_;
     }
@@ -582,17 +539,17 @@ class InlineFrameIteratorMaybeGC
                     // There is still a parent frame of this inlined frame.  All
                     // arguments (also the overflown) are the last pushed values
                     // in the parent frame.  To get the overflown arguments, we
                     // need to take them from there.
 
                     // The overflown arguments are not available in current frame.
                     // They are the last pushed arguments in the parent frame of
                     // this inlined frame.
-                    InlineFrameIteratorMaybeGC it(cx, this);
+                    InlineFrameIterator it(cx, this);
                     ++it;
                     unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
                     SnapshotIterator parent_s(it.snapshotIterator());
 
                     // Skip over all slots until we get to the last slots
                     // (= arguments slots of callee) the +3 is for [this], [returnvalue],
                     // [scopechain], and maybe +1 for [argsObj]
                     JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
@@ -668,17 +625,17 @@ class InlineFrameIteratorMaybeGC
 
         // Arguments object.
         if (script()->argumentsHasVarBinding())
             s.skip();
 
         return s.read();
     }
 
-    InlineFrameIteratorMaybeGC &operator++() {
+    InlineFrameIterator &operator++() {
         findNextFrame();
         return *this;
     }
 
     void dump() const;
 
     void resetOn(const JitFrameIterator *iter);
 
@@ -691,20 +648,18 @@ class InlineFrameIteratorMaybeGC
         return frameCount() - framesRead_;
     }
     size_t frameCount() const {
         MOZ_ASSERT(frameCount_ != UINT32_MAX);
         return frameCount_;
     }
 
   private:
-    InlineFrameIteratorMaybeGC() MOZ_DELETE;
-    InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE;
+    InlineFrameIterator() MOZ_DELETE;
+    InlineFrameIterator(const InlineFrameIterator &iter) MOZ_DELETE;
 };
-typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator;
-typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_JitFrameIterator_h */
--- a/testing/config/mozharness/linux_config.py
+++ b/testing/config/mozharness/linux_config.py
@@ -7,17 +7,18 @@ config = {
         "--appname=%(binary_path)s", "--utility-path=tests/bin",
         "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s"
     ],
     "mochitest_options": [
         "--appname=%(binary_path)s", "--utility-path=tests/bin",
         "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s",
         "--certificate-path=tests/certs", "--autorun", "--close-when-done",
         "--console-level=INFO", "--setpref=webgl.force-enabled=true",
-        "--quiet"
+        "--quiet",
+        "--use-test-media-devices"
     ],
     "webapprt_options": [
         "--app=%(app_path)s", "--utility-path=tests/bin",
         "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s",
         "--certificate-path=tests/certs", "--autorun", "--close-when-done",
         "--console-level=INFO", "--testing-modules-dir=tests/modules",
         "--quiet"
     ],
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -99,16 +99,20 @@ private:
   // Telemetry states.
   // Status of the remote response (valid or not).
   enum SERVER_RESPONSE_TYPES {
     SERVER_RESPONSE_VALID = 0,
     SERVER_RESPONSE_FAILED = 1,
     SERVER_RESPONSE_INVALID = 2,
   };
 
+  // Number of blocklist and allowlist hits we have seen.
+  uint32_t mBlocklistCount;
+  uint32_t mAllowlistCount;
+
   // The query containing metadata about the downloaded file.
   nsCOMPtr<nsIApplicationReputationQuery> mQuery;
 
   // The callback with which to report the verdict.
   nsCOMPtr<nsIApplicationReputationCallback> mCallback;
 
   // An array of strings created from certificate information used to whitelist
   // the downloaded file.
@@ -238,17 +242,17 @@ PendingDBLookup::~PendingDBLookup()
   LOG(("Destroying pending DB lookup [this = %p]", this));
   mPendingLookup = nullptr;
 }
 
 nsresult
 PendingDBLookup::LookupSpec(const nsACString& aSpec,
                             bool aAllowlistOnly)
 {
-  LOG(("Checking principal %s", aSpec.Data()));
+  LOG(("Checking principal %s [this=%p]", aSpec.Data(), this));
   mSpec = aSpec;
   mAllowlistOnly = aAllowlistOnly;
   nsresult rv = LookupSpecInternal(aSpec);
   if (NS_FAILED(rv)) {
     LOG(("Error in LookupSpecInternal"));
     return mPendingLookup->OnComplete(false, NS_OK);
   }
   // LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is
@@ -293,46 +297,51 @@ PendingDBLookup::LookupSpecInternal(cons
   }
   return dbService->Lookup(principal, tables, this);
 }
 
 NS_IMETHODIMP
 PendingDBLookup::HandleEvent(const nsACString& tables)
 {
   // HandleEvent is guaranteed to call either:
-  // 1) PendingLookup::OnComplete if the URL can be classified locally, or
-  // 2) PendingLookup::LookupNext if the URL can be cannot classified locally.
+  // 1) PendingLookup::OnComplete if the URL matches the blocklist, or
+  // 2) PendingLookup::LookupNext if the URL does not match the blocklist.
   // Blocklisting trumps allowlisting.
   nsAutoCString blockList;
   Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
   if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
+    mPendingLookup->mBlocklistCount++;
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
     LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
     return mPendingLookup->OnComplete(true, NS_OK);
   }
 
   nsAutoCString allowList;
   Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
   if (FindInReadable(allowList, tables)) {
+    mPendingLookup->mAllowlistCount++;
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
     LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
-    return mPendingLookup->OnComplete(false, NS_OK);
+    // Don't call onComplete, since blocklisting trumps allowlisting
+  } else {
+    LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(),
+         this));
+    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
   }
-
-  LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(), this));
-  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
   return mPendingLookup->LookupNext();
 }
 
 NS_IMPL_ISUPPORTS(PendingLookup,
                   nsIStreamListener,
                   nsIRequestObserver)
 
 PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
                              nsIApplicationReputationCallback* aCallback) :
+  mBlocklistCount(0),
+  mAllowlistCount(0),
   mQuery(aQuery),
   mCallback(aCallback)
 {
   LOG(("Created pending lookup [this = %p]", this));
 }
 
 PendingLookup::~PendingLookup()
 {
@@ -366,52 +375,62 @@ PendingLookup::IsBinaryFile()
     StringEndsWith(fileName, NS_LITERAL_STRING(".vbs")) ||
     StringEndsWith(fileName, NS_LITERAL_STRING(".zip"));
 }
 
 nsresult
 PendingLookup::LookupNext()
 {
   // We must call LookupNext or SendRemoteQuery upon return.
-  // Look up all of the URLs that could whitelist this download.
-  // Blacklist first.
+  // Look up all of the URLs that could allow or block this download.
+  // Blocklist first.
+  if (mBlocklistCount > 0) {
+    return OnComplete(true, NS_OK);
+  }
   int index = mAnylistSpecs.Length() - 1;
   nsCString spec;
-  bool allowlistOnly = false;
   if (index >= 0) {
-    // Check the source URI and referrer.
+    // Check the source URI, referrer and redirect chain.
     spec = mAnylistSpecs[index];
     mAnylistSpecs.RemoveElementAt(index);
-  } else {
-    // Check the allowlists next.
-    index = mAllowlistSpecs.Length() - 1;
-    if (index >= 0) {
-      allowlistOnly = true;
-      spec = mAllowlistSpecs[index];
-      mAllowlistSpecs.RemoveElementAt(index);
-    }
+    nsRefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
+    return lookup->LookupSpec(spec, false);
+  }
+  // If any of mAnylistSpecs matched the blocklist, go ahead and block.
+  if (mBlocklistCount > 0) {
+    return OnComplete(true, NS_OK);
   }
+  // If any of mAnylistSpecs matched the allowlist, go ahead and pass.
+  if (mAllowlistCount > 0) {
+    return OnComplete(false, NS_OK);
+  }
+  // Only binary signatures remain.
+  index = mAllowlistSpecs.Length() - 1;
   if (index >= 0) {
+    spec = mAllowlistSpecs[index];
+    LOG(("PendingLookup::LookupNext: checking %s on allowlist", spec.get()));
+    mAllowlistSpecs.RemoveElementAt(index);
     nsRefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
-    return lookup->LookupSpec(spec, allowlistOnly);
+    return lookup->LookupSpec(spec, true);
   }
 #ifdef XP_WIN
   // There are no more URIs to check against local list. If the file is not
   // eligible for remote lookup, bail.
   if (!IsBinaryFile()) {
     LOG(("Not eligible for remote lookups [this=%x]", this));
     return OnComplete(false, NS_OK);
   }
   // Send the remote query if we are on Windows.
   nsresult rv = SendRemoteQuery();
   if (NS_FAILED(rv)) {
     return OnComplete(false, rv);
   }
   return NS_OK;
 #else
+  LOG(("PendingLookup: Nothing left to check [this=%p]", this));
   return OnComplete(false, NS_OK);
 #endif
 }
 
 nsCString
 PendingLookup::EscapeCertificateAttribute(const nsACString& aAttribute)
 {
   // Escape '/' because it's a field separator, and '%' because Chrome does
@@ -625,17 +644,17 @@ PendingLookup::DoLookupInternal()
     mAnylistSpecs.AppendElement(spec);
     resource->set_referrer(spec.get());
   }
   nsCOMPtr<nsIArray> redirects;
   rv = mQuery->GetRedirects(getter_AddRefs(redirects));
   if (redirects) {
     AddRedirects(redirects);
   } else {
-    LOG(("ApplicationReputation: Got no redirects"));
+    LOG(("ApplicationReputation: Got no redirects [this=%p]", this));
   }
 
   // Extract the signature and parse certificates so we can use it to check
   // whitelists.
   nsCOMPtr<nsIArray> sigArray;
   rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -971,17 +990,17 @@ ApplicationReputationService::Applicatio
 ApplicationReputationService::~ApplicationReputationService() {
   LOG(("Application reputation service shutting down"));
 }
 
 NS_IMETHODIMP
 ApplicationReputationService::QueryReputation(
     nsIApplicationReputationQuery* aQuery,
     nsIApplicationReputationCallback* aCallback) {
-  LOG(("Starting application reputation check"));
+  LOG(("Starting application reputation check [query=%p]", aQuery));
   NS_ENSURE_ARG_POINTER(aQuery);
   NS_ENSURE_ARG_POINTER(aCallback);
 
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
   nsresult rv = QueryReputationInternal(aQuery, aCallback);
   if (NS_FAILED(rv)) {
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
       false);
--- a/toolkit/components/downloads/test/unit/test_app_rep.js
+++ b/toolkit/components/downloads/test/unit/test_app_rep.js
@@ -63,21 +63,25 @@ function run_test() {
                              "http://localhost:4444/download");
   // Ensure safebrowsing is enabled for this test, even if the app
   // doesn't have it enabled.
   Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", true);
   do_register_cleanup(function() {
     Services.prefs.clearUserPref("browser.safebrowsing.malware.enabled");
   });
 
-  // Set download_block_table explicitly.
+  // Set block and allow tables explicitly, since the allowlist is normally
+  // disabled on non-Windows platforms.
   Services.prefs.setCharPref("urlclassifier.downloadBlockTable",
                              "goog-badbinurl-shavar");
+  Services.prefs.setCharPref("urlclassifier.downloadAllowTable",
+                             "goog-downloadwhite-digest256");
   do_register_cleanup(function() {
     Services.prefs.clearUserPref("urlclassifier.downloadBlockTable");
+    Services.prefs.clearUserPref("urlclassifier.downloadAllowTable");
   });
 
   gHttpServ = new HttpServer();
   gHttpServ.registerDirectory("/", do_get_cwd());
   gHttpServ.registerPathHandler("/download", function(request, response) {
     do_throw("This test should never make a remote lookup");
   });
   gHttpServ.start(4444);
@@ -92,19 +96,23 @@ function check_telemetry(aCount,
                 .getService(Ci.nsITelemetry)
                 .getHistogramById("APPLICATION_REPUTATION_COUNT")
                 .snapshot();
   do_check_eq(count.counts[1], aCount);
   let local = Cc["@mozilla.org/base/telemetry;1"]
                 .getService(Ci.nsITelemetry)
                 .getHistogramById("APPLICATION_REPUTATION_LOCAL")
                 .snapshot();
-  do_check_eq(local.counts[ALLOW_LIST], aListCounts[ALLOW_LIST]);
-  do_check_eq(local.counts[BLOCK_LIST], aListCounts[BLOCK_LIST]);
-  do_check_eq(local.counts[NO_LIST], aListCounts[NO_LIST]);
+  do_check_eq(local.counts[ALLOW_LIST], aListCounts[ALLOW_LIST],
+              "Allow list counts don't match");
+  do_check_eq(local.counts[BLOCK_LIST], aListCounts[BLOCK_LIST],
+              "Block list counts don't match");
+  do_check_eq(local.counts[NO_LIST], aListCounts[NO_LIST],
+              "No list counts don't match");
+
   let shouldBlock = Cc["@mozilla.org/base/telemetry;1"]
                 .getService(Ci.nsITelemetry)
                 .getHistogramById("APPLICATION_REPUTATION_SHOULD_BLOCK")
                 .snapshot();
   // SHOULD_BLOCK = true
   do_check_eq(shouldBlock.counts[1], aShouldBlockCount);
   // Sanity check that SHOULD_BLOCK total adds up to the COUNT.
   do_check_eq(shouldBlock.counts[0] + shouldBlock.counts[1], aCount);
@@ -195,19 +203,19 @@ add_test(function test_local_list() {
     response.bodyOutputStream.write(blob, blob.length);
   });
 
   let streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
     .getService(Ci.nsIUrlClassifierStreamUpdater);
   streamUpdater.updateUrl = "http://localhost:4444/downloads";
 
   // Load up some update chunks for the safebrowsing server to serve.
-  // This chunk contains the hash of whitelisted.com/.
+  // This chunk contains the hash of blocklisted.com/.
   registerTableUpdate("goog-badbinurl-shavar", "data/block_digest.chunk");
-  // This chunk contains the hash of blocklisted.com/.
+  // This chunk contains the hash of whitelisted.com/.
   registerTableUpdate("goog-downloadwhite-digest256", "data/digest.chunk");
 
   // Download some updates, and don't continue until the downloads are done.
   function updateSuccess(aEvent) {
     // Timeout of n:1000 is constructed in processUpdateRequest above and
     // passed back in the callback in nsIUrlClassifierStreamUpdater on success.
     do_check_eq("1000", aEvent);
     do_print("All data processed");
@@ -294,25 +302,26 @@ add_test(function test_blocklist_trumps_
 });
 
 add_test(function test_redirect_on_blocklist() {
   Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
                              "http://localhost:4444/download");
   let counts = get_telemetry_counts();
   let listCounts = counts.listCounts;
   listCounts[BLOCK_LIST]++;
+  listCounts[ALLOW_LIST]++;
   let secman = Services.scriptSecurityManager;
   let badRedirects = Cc["@mozilla.org/array;1"]
                        .createInstance(Ci.nsIMutableArray);
-  badRedirects.appendElement(secman.getNoAppCodebasePrincipal(whitelistedURI),
-                             false);
   badRedirects.appendElement(secman.getNoAppCodebasePrincipal(exampleURI),
                              false);
   badRedirects.appendElement(secman.getNoAppCodebasePrincipal(blocklistedURI),
                              false);
+  badRedirects.appendElement(secman.getNoAppCodebasePrincipal(whitelistedURI),
+                             false);
   gAppRep.queryReputation({
     sourceURI: whitelistedURI,
     referrerURI: exampleURI,
     redirects: badRedirects,
     fileSize: 12,
   }, function onComplete(aShouldBlock, aStatus) {
     do_check_eq(Cr.NS_OK, aStatus);
     do_check_true(aShouldBlock);
--- a/toolkit/components/url-classifier/LookupCache.cpp
+++ b/toolkit/components/url-classifier/LookupCache.cpp
@@ -176,17 +176,17 @@ LookupCache::Build(AddPrefixArray& aAddP
 void
 LookupCache::Dump()
 {
   if (!LOG_ENABLED())
     return;
 
   for (uint32_t i = 0; i < mCompletions.Length(); i++) {
     nsAutoCString str;
-    mCompletions[i].ToString(str);
+    mCompletions[i].ToHexString(str);
     LOG(("Completion: %s", str.get()));
   }
 }
 #endif
 
 nsresult
 LookupCache::Has(const Completion& aCompletion,
                  bool* aHas, bool* aComplete)