Bug 1115495 - Part 1: Support app info for PAC. r=mcmanus
authorShian-Yow Wu <swu@mozilla.com>
Thu, 26 Mar 2015 15:11:05 +0800
changeset 266108 c6a9943ddf801054b001c1ece5d20b9087a37528
parent 266107 ae55f70e6baa3077035a63de3c60de9967b2b619
child 266109 748b96aa103bf1e8d785504eda0e43bc348f6880
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1115495
milestone39.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
Bug 1115495 - Part 1: Support app info for PAC. r=mcmanus
netwerk/base/ProxyAutoConfig.cpp
netwerk/base/ProxyAutoConfig.h
netwerk/base/nsPACMan.cpp
netwerk/base/nsPACMan.h
netwerk/base/nsProtocolProxyService.cpp
netwerk/base/nsProtocolProxyService.h
netwerk/test/unit/test_protocolproxyservice.js
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -457,16 +457,73 @@ bool PACMyIpAddress(JSContext *cx, unsig
   if (!GetRunning()) {
     NS_WARNING("PAC myIPAddress without a running ProxyAutoConfig object");
     return false;
   }
 
   return GetRunning()->MyIPAddress(args);
 }
 
+// myAppId() javascript implementation
+static
+bool PACMyAppId(JSContext *cx, unsigned int argc, JS::Value *vp)
+{
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+  if (NS_IsMainThread()) {
+    NS_WARNING("PACMyAppId on Main Thread. How did that happen?");
+    return false;
+  }
+
+  if (!GetRunning()) {
+    NS_WARNING("PACMyAppId without a running ProxyAutoConfig object");
+    return false;
+  }
+
+  return GetRunning()->MyAppId(args);
+}
+
+// myAppOrigin() javascript implementation
+static
+bool PACMyAppOrigin(JSContext *cx, unsigned int argc, JS::Value *vp)
+{
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+  if (NS_IsMainThread()) {
+    NS_WARNING("PACMyAppOrigin on Main Thread. How did that happen?");
+    return false;
+  }
+
+  if (!GetRunning()) {
+    NS_WARNING("PACMyAppOrigin without a running ProxyAutoConfig object");
+    return false;
+  }
+
+  return GetRunning()->MyAppOrigin(args);
+}
+
+// IsInBrowser() javascript implementation
+static
+bool PACIsInBrowser(JSContext *cx, unsigned int argc, JS::Value *vp)
+{
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+  if (NS_IsMainThread()) {
+    NS_WARNING("PACIsInBrowser on Main Thread. How did that happen?");
+    return false;
+  }
+
+  if (!GetRunning()) {
+    NS_WARNING("PACIsInBrowser without a running ProxyAutoConfig object");
+    return false;
+  }
+
+  return GetRunning()->IsInBrowser(args);
+}
+
 // proxyAlert(msg) javascript implementation
 static
 bool PACProxyAlert(JSContext *cx, unsigned int argc, JS::Value *vp)
 {
   JS::CallArgs args = CallArgsFromVp(argc, vp);
 
   if (!args.requireAtLeast(cx, "alert", 1))
     return false;
@@ -490,17 +547,19 @@ bool PACProxyAlert(JSContext *cx, unsign
 }
 
 static const JSFunctionSpec PACGlobalFunctions[] = {
   JS_FS("dnsResolve", PACDnsResolve, 1, 0),
 
   // a global "var pacUseMultihomedDNS = true;" will change behavior
   // of myIpAddress to actively use DNS
   JS_FS("myIpAddress", PACMyIpAddress, 0, 0),
-
+  JS_FS("myAppId", PACMyAppId, 0, 0),
+  JS_FS("myAppOrigin", PACMyAppOrigin, 0, 0),
+  JS_FS("isInBrowser", PACIsInBrowser, 0, 0),
   JS_FS("alert", PACProxyAlert, 1, 0),
   JS_FS_END
 };
 
 // JSRuntimeWrapper is a c++ object that manages the runtime and context
 // for the JS engine used on the PAC thread. It is initialized and destroyed
 // on the PAC thread.
 class JSRuntimeWrapper
@@ -698,32 +757,38 @@ ProxyAutoConfig::SetupJS()
   mPACURI.Truncate();
 
   return NS_OK;
 }
 
 nsresult
 ProxyAutoConfig::GetProxyForURI(const nsCString &aTestURI,
                                 const nsCString &aTestHost,
+                                uint32_t aAppId,
+                                const nsString &aAppOrigin,
+                                bool aIsInBrowser,
                                 nsACString &result)
 {
   if (mJSNeedsSetup)
     SetupJS();
 
   if (!mJSRuntime || !mJSRuntime->IsOK())
     return NS_ERROR_NOT_AVAILABLE;
 
   JSContext *cx = mJSRuntime->Context();
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, mJSRuntime->Global());
 
   // the sRunning flag keeps a new PAC file from being installed
   // while the event loop is spinning on a DNS function. Don't early return.
   SetRunning(this);
   mRunningHost = aTestHost;
+  mRunningAppId = aAppId;
+  mRunningAppOrigin = aAppOrigin;
+  mRunningIsInBrowser = aIsInBrowser;
 
   nsresult rv = NS_ERROR_FAILURE;
   JS::RootedString uriString(cx, JS_NewStringCopyZ(cx, aTestURI.get()));
   JS::RootedString hostString(cx, JS_NewStringCopyZ(cx, aTestHost.get()));
 
   if (uriString && hostString) {
     JS::AutoValueArray<2> args(cx);
     args[0].setString(uriString);
@@ -923,10 +988,38 @@ ProxyAutoConfig::MyIPAddress(const JS::C
   if (!dottedDecimalString) {
     return false;
   }
 
   aArgs.rval().setString(dottedDecimalString);
   return true;
 }
 
+bool
+ProxyAutoConfig::MyAppId(const JS::CallArgs &aArgs)
+{
+  aArgs.rval().setNumber(mRunningAppId);
+  return true;
+}
+
+bool
+ProxyAutoConfig::MyAppOrigin(const JS::CallArgs &aArgs)
+{
+  JSContext *cx = mJSRuntime->Context();
+  JSString *origin =
+    JS_NewStringCopyZ(cx, NS_ConvertUTF16toUTF8(mRunningAppOrigin).get());
+  if (!origin) {
+    return false;
+  }
+
+  aArgs.rval().setString(origin);
+  return true;
+}
+
+bool
+ProxyAutoConfig::IsInBrowser(const JS::CallArgs &aArgs)
+{
+  aArgs.rval().setBoolean(mRunningIsInBrowser);
+  return true;
+}
+
 } // namespace mozilla
 } // namespace mozilla::net
--- a/netwerk/base/ProxyAutoConfig.h
+++ b/netwerk/base/ProxyAutoConfig.h
@@ -30,16 +30,19 @@ public:
   ~ProxyAutoConfig();
 
   nsresult Init(const nsCString &aPACURI,
                 const nsCString &aPACScript);
   void     SetThreadLocalIndex(uint32_t index);
   void     Shutdown();
   void     GC();
   bool     MyIPAddress(const JS::CallArgs &aArgs);
+  bool     MyAppId(const JS::CallArgs &aArgs);
+  bool     MyAppOrigin(const JS::CallArgs &aArgs);
+  bool     IsInBrowser(const JS::CallArgs &aArgs);
   bool     ResolveAddress(const nsCString &aHostName,
                           NetAddr *aNetAddr, unsigned int aTimeout);
 
   /**
    * Get the proxy string for the specified URI.  The proxy string is
    * given by the following:
    *
    *   result      = proxy-spec *( proxy-sep proxy-spec )
@@ -63,22 +66,31 @@ public:
    *
    * XXX add support for IPv6 address literals.
    * XXX quote whatever the official standard is for PAC.
    *
    * @param aTestURI
    *        The URI as an ASCII string to test.
    * @param aTestHost
    *        The ASCII hostname to test.
+   * @param aAppId
+   *        The id of the app requesting connection.
+   * @param aAppOrigin
+   *        The origin of the app requesting connection.
+   * @param aIsInBrowser
+   *        True if the iframe has mozbrowser but has no mozapp attribute.
    *
    * @param result
    *        result string as defined above.
    */
   nsresult GetProxyForURI(const nsCString &aTestURI,
                           const nsCString &aTestHost,
+                          uint32_t aAppId,
+                          const nsString &aAppOrigin,
+                          bool aIsInBrowser,
                           nsACString &result);
 
 private:
   // allow 665ms for myipaddress dns queries. That's 95th percentile.
   const static unsigned int kTimeout = 665;
 
   // used to compile the PAC file and setup the execution context
   nsresult SetupJS();
@@ -88,14 +100,17 @@ private:
                           const JS::CallArgs &aArgs, bool* aResult);
 
   JSRuntimeWrapper *mJSRuntime;
   bool              mJSNeedsSetup;
   bool              mShutdown;
   nsCString         mPACScript;
   nsCString         mPACURI;
   nsCString         mRunningHost;
+  uint32_t          mRunningAppId;
+  nsString          mRunningAppOrigin;
+  bool              mRunningIsInBrowser;
   nsCOMPtr<nsITimer> mTimer;
 };
 
 }} // namespace mozilla::net
 
 #endif  // ProxyAutoConfig_h__
--- a/netwerk/base/nsPACMan.cpp
+++ b/netwerk/base/nsPACMan.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsPACMan.h"
+#include "mozIApplication.h"
+#include "nsIAppsService.h"
 #include "nsThreadUtils.h"
 #include "nsIAuthPrompt.h"
 #include "nsIPromptFactory.h"
 #include "nsIHttpChannel.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsNetUtil.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
@@ -226,26 +228,41 @@ private:
   bool                 mSetupPAC;
   nsCString            mSetupPACData;
   nsCString            mSetupPACURI;
 };
 
 //-----------------------------------------------------------------------------
 
 PendingPACQuery::PendingPACQuery(nsPACMan *pacMan, nsIURI *uri,
+                                 uint32_t appId, bool isInBrowser,
                                  nsPACManCallback *callback,
                                  bool mainThreadResponse)
   : mPACMan(pacMan)
+  , mAppId(appId)
+  , mIsInBrowser(isInBrowser)
   , mCallback(callback)
   , mOnMainThreadOnly(mainThreadResponse)
 {
   uri->GetAsciiSpec(mSpec);
   uri->GetAsciiHost(mHost);
   uri->GetScheme(mScheme);
   uri->GetPort(&mPort);
+
+  nsCOMPtr<nsIAppsService> appsService =
+      do_GetService(APPS_SERVICE_CONTRACTID);
+  if (!appsService) {
+    return;
+  }
+  nsCOMPtr<mozIApplication> mozApp;
+  nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(mozApp));
+  if (NS_FAILED(rv) || !mozApp) {
+      return;
+  }
+  mozApp->GetOrigin(mAppOrigin);
 }
 
 void
 PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
 {
   if (!mCallback)
     return;
   nsRefPtr<ExecuteCallback> runnable = new ExecuteCallback(mCallback, status);
@@ -324,30 +341,32 @@ nsPACMan::Shutdown()
   CancelExistingLoad();
   PostCancelPendingQ(NS_ERROR_ABORT);
 
   nsRefPtr<WaitForThreadShutdown> runnable = new WaitForThreadShutdown(this);
   NS_DispatchToMainThread(runnable);
 }
 
 nsresult
-nsPACMan::AsyncGetProxyForURI(nsIURI *uri, nsPACManCallback *callback,
+nsPACMan::AsyncGetProxyForURI(nsIURI *uri, uint32_t appId,
+                              bool isInBrowser, nsPACManCallback *callback,
                               bool mainThreadResponse)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   if (mShutdown)
     return NS_ERROR_NOT_AVAILABLE;
 
   // Maybe Reload PAC
   if (!mPACURISpec.IsEmpty() && !mScheduledReload.IsNull() &&
       TimeStamp::Now() > mScheduledReload)
     LoadPACFromURI(EmptyCString());
 
   nsRefPtr<PendingPACQuery> query =
-    new PendingPACQuery(this, uri, callback, mainThreadResponse);
+    new PendingPACQuery(this, uri, appId, isInBrowser, callback,
+                        mainThreadResponse);
 
   if (IsPACURI(uri)) {
     // deal with this directly instead of queueing it
     query->Complete(NS_OK, EmptyCString());
     return NS_OK;
   }
 
   return mPACThread->Dispatch(query, nsIEventTarget::DISPATCH_NORMAL);
@@ -590,17 +609,20 @@ nsPACMan::ProcessPending()
                                   query->mHost, query->mPort,
                                   pacString))) {
     query->Complete(NS_OK, pacString);
     completed = true;
   }
 
   // the systemproxysettings didn't complete the resolution. try via PAC
   if (!completed) {
-    nsresult status = mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString);
+    nsresult status = mPAC.GetProxyForURI(query->mSpec, query->mHost,
+                                          query->mAppId, query->mAppOrigin,
+                                          query->mIsInBrowser,
+                                          pacString);
     query->Complete(status, pacString);
   }
 
   mInProgress = false;
   return true;
 }
 
 NS_IMPL_ISUPPORTS(nsPACMan, nsIStreamLoaderObserver,
--- a/netwerk/base/nsPACMan.h
+++ b/netwerk/base/nsPACMan.h
@@ -49,32 +49,40 @@ public:
                                const nsCString &pacString,
                                const nsCString &newPACURL) = 0;
 };
 
 class PendingPACQuery final : public nsRunnable,
                                   public mozilla::LinkedListElement<PendingPACQuery>
 {
 public:
-  PendingPACQuery(nsPACMan *pacMan, nsIURI *uri,
-                  nsPACManCallback *callback, bool mainThreadResponse);
-
+  PendingPACQuery(nsPACMan *pacMan, nsIURI *uri, uint32_t appId,
+                  bool isInBrowser, nsPACManCallback *callback,
+                  bool mainThreadResponse);
+ 
   // can be called from either thread
   void Complete(nsresult status, const nsCString &pacString);
   void UseAlternatePACFile(const nsCString &pacURL);
 
   nsCString                  mSpec;
   nsCString                  mScheme;
   nsCString                  mHost;
   int32_t                    mPort;
 
   NS_IMETHOD Run(void);     /* nsRunnable */
 
 private:
   nsPACMan                  *mPACMan;  // weak reference
+
+public:
+  uint32_t                   mAppId;
+  bool                       mIsInBrowser;
+  nsString                   mAppOrigin;
+
+private:
   nsRefPtr<nsPACManCallback> mCallback;
   bool                       mOnMainThreadOnly;
 };
 
 /**
  * This class provides an abstraction layer above the PAC thread.  The methods
  * defined on this class are intended to be called on the main thread only.
  */
@@ -98,22 +106,28 @@ public:
   /**
    * This method queries a PAC result asynchronously.  The callback runs on the
    * calling thread.  If the PAC file has not yet been loaded, then this method
    * will queue up the request, and complete it once the PAC file has been
    * loaded.
    * 
    * @param uri
    *        The URI to query.
+   * @param appId
+   *        The appId of the app making the connection.
+   * @param isInBrowser
+   *        True if the iframe has mozbrowser but has no mozapp attribute.
    * @param callback
    *        The callback to run once the PAC result is available.
    * @param mustCallbackOnMainThread
    *        If set to false the callback can be made from the PAC thread
    */
-  nsresult AsyncGetProxyForURI(nsIURI *uri, nsPACManCallback *callback,
+  nsresult AsyncGetProxyForURI(nsIURI *uri, uint32_t appId,
+                               bool isInBrowser,
+                               nsPACManCallback *callback,
                                bool mustCallbackOnMainThread);
 
   /**
    * This method may be called to reload the PAC file.  While we are loading
    * the PAC file, any asynchronous PAC queries will be queued up to be
    * processed once the PAC file finishes loading.
    *
    * @param pacSpec
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -100,24 +100,27 @@ GetProxyURI(nsIChannel *channel, nsIURI 
 class nsAsyncResolveRequest final : public nsIRunnable
                                       , public nsPACManCallback
                                       , public nsICancelable
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
     nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
+                          uint32_t aAppId, bool aIsInBrowser,
                           uint32_t aResolveFlags,
                           nsIProtocolProxyCallback *callback)
         : mStatus(NS_OK)
         , mDispatched(false)
         , mResolveFlags(aResolveFlags)
         , mPPS(pps)
         , mXPComPPS(pps)
         , mChannel(channel)
+        , mAppId(aAppId)
+        , mIsInBrowser(aIsInBrowser)
         , mCallback(callback)
     {
         NS_ASSERTION(mCallback, "null callback");
     }
 
 private:
     ~nsAsyncResolveRequest()
     {
@@ -259,18 +262,23 @@ private:
             nsCOMPtr<nsIURI> proxyURI;
             GetProxyURI(mChannel, getter_AddRefs(proxyURI));
 
             // trigger load of new pac url
             nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
             if (NS_SUCCEEDED(rv)) {
                 // now that the load is triggered, we can resubmit the query
                 nsRefPtr<nsAsyncResolveRequest> newRequest =
-                    new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
-                rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, newRequest, true);
+                    new nsAsyncResolveRequest(mPPS, mChannel, mAppId,
+                                              mIsInBrowser, mResolveFlags,
+                                              mCallback);
+                rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, mAppId,
+                                                        mIsInBrowser,
+                                                        newRequest,
+                                                        true);
             }
 
             if (NS_FAILED(rv))
                 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
 
             // do not call onproxyavailable() in SUCCESS case - the newRequest will
             // take care of that
         }
@@ -297,16 +305,18 @@ private:
     nsCString mPACString;
     nsCString mPACURL;
     bool      mDispatched;
     uint32_t  mResolveFlags;
 
     nsProtocolProxyService            *mPPS;
     nsCOMPtr<nsIProtocolProxyService>  mXPComPPS;
     nsCOMPtr<nsIChannel>               mChannel;
+    uint32_t                           mAppId;
+    bool                               mIsInBrowser;
     nsCOMPtr<nsIProtocolProxyCallback> mCallback;
     nsCOMPtr<nsIProxyInfo>             mProxyInfo;
 };
 
 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
 
 //----------------------------------------------------------------------------
 
@@ -1148,31 +1158,33 @@ nsProtocolProxyService::DeprecatedBlocki
 
     nsCOMPtr<nsIProxyInfo> pi;
     bool usePACThread;
 
     // SystemProxySettings and PAC files can block the main thread
     // but if neither of them are in use, we can just do the work
     // right here and directly invoke the callback
 
-    rv = Resolve_Internal(aChannel, info, aFlags, &usePACThread, getter_AddRefs(pi));
+    rv = Resolve_Internal(aChannel, NECKO_NO_APP_ID, false, info, aFlags,
+                          &usePACThread, getter_AddRefs(pi));
     if (NS_FAILED(rv))
         return rv;
 
     if (!usePACThread || !mPACMan) {
         ApplyFilters(aChannel, info, pi);
         pi.forget(retval);
         return NS_OK;
     }
 
     // Use the PAC thread to do the work, so we don't have to reimplement that
     // code, but block this thread on that completion.
     nsRefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
     ctx->Lock();
-    if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(uri, ctx, false))) {
+    if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(uri, NECKO_NO_APP_ID, false,
+                                                  ctx, false))) {
         // this can really block the main thread, so cap it at 3 seconds
        ctx->Wait();
     }
     ctx->Unlock();
     if (!ctx->mCompleted)
         return NS_ERROR_FAILURE;
     if (NS_FAILED(ctx->mStatus))
         return ctx->mStatus;
@@ -1214,33 +1226,39 @@ nsProtocolProxyService::AsyncResolveInte
 {
     NS_ENSURE_ARG_POINTER(channel);
     NS_ENSURE_ARG_POINTER(callback);
 
     nsCOMPtr<nsIURI> uri;
     nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
     if (NS_FAILED(rv)) return rv;
 
+    uint32_t appId = NECKO_NO_APP_ID;
+    bool isInBrowser = false;
+    NS_GetAppInfo(channel, &appId, &isInBrowser);
+
     *result = nullptr;
     nsRefPtr<nsAsyncResolveRequest> ctx =
-        new nsAsyncResolveRequest(this, channel, flags, callback);
+        new nsAsyncResolveRequest(this, channel, appId, isInBrowser, flags,
+                                  callback);
 
     nsProtocolInfo info;
     rv = GetProtocolInfo(uri, &info);
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIProxyInfo> pi;
     bool usePACThread;
 
     // SystemProxySettings and PAC files can block the main thread
     // but if neither of them are in use, we can just do the work
     // right here and directly invoke the callback
 
-    rv = Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
+    rv = Resolve_Internal(channel, appId, isInBrowser, info, flags,
+                          &usePACThread, getter_AddRefs(pi));
     if (NS_FAILED(rv))
         return rv;
 
     if (!usePACThread || !mPACMan) {
         // we can do it locally
         ApplyFilters(channel, info, pi);
         ctx->SetResult(NS_OK, pi);
         if (isSyncOK) {
@@ -1251,17 +1269,17 @@ nsProtocolProxyService::AsyncResolveInte
         rv = ctx->DispatchCallback();
         if (NS_SUCCEEDED(rv))
             ctx.forget(result);
         return rv;
     }
 
     // else kick off a PAC thread query
 
-    rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true);
+    rv = mPACMan->AsyncGetProxyForURI(uri, appId, isInBrowser, ctx, true);
     if (NS_SUCCEEDED(rv))
         ctx.forget(result);
     return rv;
 }
 
 // nsIProtocolProxyService
 NS_IMETHODIMP
 nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
@@ -1672,16 +1690,18 @@ nsProtocolProxyService::NewProxyInfo_Int
     failover.swap(proxyInfo->mNext);
 
     NS_ADDREF(*aResult = proxyInfo);
     return NS_OK;
 }
 
 nsresult
 nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
+                                         uint32_t appId,
+                                         bool isInBrowser,
                                          const nsProtocolInfo &info,
                                          uint32_t flags,
                                          bool *usePACThread,
                                          nsIProxyInfo **result)
 {
     NS_ENSURE_ARG_POINTER(channel);
     nsresult rv = SetupPACThread();
     if (NS_FAILED(rv))
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -200,31 +200,37 @@ protected:
     /**
      * This method is an internal version of Resolve that does not query PAC.
      * It performs all of the built-in processing, and reports back to the
      * caller with either the proxy info result or a flag to instruct the
      * caller to use PAC instead.
      *
      * @param channel
      *        The channel to test.
+     * @param appId
+     *        The id of the app making the query.
+     * @param isInBrowser
+     *        True if the iframe has mozbrowser but has no mozapp attribute.
      * @param info
      *        Information about the URI's protocol.
      * @param flags
      *        The flags passed to either the resolve or the asyncResolve method.
      * @param usePAC
      *        If this flag is set upon return, then PAC should be queried to
      *        resolve the proxy info.
      * @param result
      *        The resulting proxy info or null.
      */
     nsresult Resolve_Internal(nsIChannel *channel,
-                                          const nsProtocolInfo &info,
-                                          uint32_t flags,
-                                          bool *usePAC, 
-                                          nsIProxyInfo **result);
+                              uint32_t appId,
+                              bool isInBrowser,
+                              const nsProtocolInfo &info,
+                              uint32_t flags,
+                              bool *usePAC,
+                              nsIProxyInfo **result);
 
     /**
      * This method applies the registered filters to the given proxy info
      * list, and returns a possibly modified list.
      *
      * @param channel
      *        The channel corresponding to this proxy info list.
      * @param info
--- a/netwerk/test/unit/test_protocolproxyservice.js
+++ b/netwerk/test/unit/test_protocolproxyservice.js
@@ -624,17 +624,62 @@ function run_pac3_test() {
                                 null,      // aTriggeringPrincipal
                                 Ci.nsILoadInfo.SEC_NORMAL,
                                 Ci.nsIContentPolicy.TYPE_OTHER);
 
   // Configure PAC
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
   prefs.setBoolPref("network.proxy.proxy_over_tls", false);
 
-  var req = pps.asyncResolve(channel, 0, new TestResolveCallback(null, finish_pac_test));
+  var req = pps.asyncResolve(channel, 0, new TestResolveCallback(null, run_pac4_test));
+}
+
+function run_pac4_test() {
+  var appId = 10;
+  var isInBrowser = true;
+  var appOrigin = "apps://browser.gaiamobile.com";
+
+  // We have to setup a profile, otherwise indexed db used by webapps
+  // will throw random exception when trying to get profile folder.
+  do_get_profile();
+
+  // We also need a valid nsIXulAppInfo service as Webapps.jsm is querying it.
+  Cu.import("resource://testing-common/AppInfo.jsm");
+  updateAppInfo();
+
+  // Mock getAppByLocalId() to return testing app origin.
+  Cu.import("resource://gre/modules/AppsUtils.jsm");
+  AppsUtils.getAppByLocalId = function(aAppId) {
+    var app = { origin: appOrigin };
+    return app;
+  };
+
+  var pac = 'data:text/plain,' +
+            'function FindProxyForURL(url, host) {' +
+            ' if (myAppId() == ' + appId +
+            ' && isInBrowser() == ' + isInBrowser +
+            ' && myAppOrigin() == "' + appOrigin + '")' +
+            '   return "PROXY foopy:8080; DIRECT";' +
+            '}';
+  var channel = ios.newChannel2("http://www.mozilla.org/",
+                                null,
+                                null,
+                                null,      // aLoadingNode
+                                Services.scriptSecurityManager.getSystemPrincipal(),
+                                null,      // aTriggeringPrincipal
+                                Ci.nsILoadInfo.SEC_NORMAL,
+                                Ci.nsIContentPolicy.TYPE_OTHER);
+  channel.notificationCallbacks =
+    AppsUtils.createLoadContext(appId, isInBrowser);
+
+  // Configure PAC
+  prefs.setIntPref("network.proxy.type", 2);
+  prefs.setCharPref("network.proxy.autoconfig_url", pac);
+
+  var req = pps.asyncResolve(channel, 0, new TestResolveCallback("http", finish_pac_test));
 }
 
 function finish_pac_test() {
   prefs.setBoolPref("network.proxy.proxy_over_tls", originalTLSProxy);
   run_pac_cancel_test();
 }
 
 function TestResolveCancelationCallback() {