security/manager/boot/src/nsSecureBrowserUIImpl.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/boot/src/nsSecureBrowserUIImpl.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1698 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifdef MOZ_LOGGING
    1.10 +#define FORCE_PR_LOG
    1.11 +#endif
    1.12 +
    1.13 +#include "nspr.h"
    1.14 +#include "prlog.h"
    1.15 +
    1.16 +#include "nsISecureBrowserUI.h"
    1.17 +#include "nsSecureBrowserUIImpl.h"
    1.18 +#include "nsCOMPtr.h"
    1.19 +#include "nsIInterfaceRequestor.h"
    1.20 +#include "nsIInterfaceRequestorUtils.h"
    1.21 +#include "nsIServiceManager.h"
    1.22 +#include "nsCURILoader.h"
    1.23 +#include "nsIDocShell.h"
    1.24 +#include "nsIDocShellTreeItem.h"
    1.25 +#include "nsIDocument.h"
    1.26 +#include "nsIPrincipal.h"
    1.27 +#include "nsIDOMElement.h"
    1.28 +#include "nsPIDOMWindow.h"
    1.29 +#include "nsIContent.h"
    1.30 +#include "nsIWebProgress.h"
    1.31 +#include "nsIWebProgressListener.h"
    1.32 +#include "nsIChannel.h"
    1.33 +#include "nsIHttpChannel.h"
    1.34 +#include "nsIFileChannel.h"
    1.35 +#include "nsIWyciwygChannel.h"
    1.36 +#include "nsIFTPChannel.h"
    1.37 +#include "nsITransportSecurityInfo.h"
    1.38 +#include "nsISSLStatus.h"
    1.39 +#include "nsIURI.h"
    1.40 +#include "nsISecurityEventSink.h"
    1.41 +#include "nsIPrompt.h"
    1.42 +#include "nsIFormSubmitObserver.h"
    1.43 +#include "nsISecurityWarningDialogs.h"
    1.44 +#include "nsISecurityInfoProvider.h"
    1.45 +#include "imgIRequest.h"
    1.46 +#include "nsThreadUtils.h"
    1.47 +#include "nsNetUtil.h"
    1.48 +#include "nsNetCID.h"
    1.49 +#include "nsCRT.h"
    1.50 +
    1.51 +using namespace mozilla;
    1.52 +
    1.53 +#define IS_SECURE(state) ((state & 0xFFFF) == STATE_IS_SECURE)
    1.54 +
    1.55 +#if defined(PR_LOGGING)
    1.56 +//
    1.57 +// Log module for nsSecureBrowserUI logging...
    1.58 +//
    1.59 +// To enable logging (see prlog.h for full details):
    1.60 +//
    1.61 +//    set NSPR_LOG_MODULES=nsSecureBrowserUI:5
    1.62 +//    set NSPR_LOG_FILE=nspr.log
    1.63 +//
    1.64 +// this enables PR_LOG_DEBUG level information and places all output in
    1.65 +// the file nspr.log
    1.66 +//
    1.67 +PRLogModuleInfo* gSecureDocLog = nullptr;
    1.68 +#endif /* PR_LOGGING */
    1.69 +
    1.70 +struct RequestHashEntry : PLDHashEntryHdr {
    1.71 +    void *r;
    1.72 +};
    1.73 +
    1.74 +static bool
    1.75 +RequestMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
    1.76 +                         const void *key)
    1.77 +{
    1.78 +  const RequestHashEntry *entry = static_cast<const RequestHashEntry*>(hdr);
    1.79 +  return entry->r == key;
    1.80 +}
    1.81 +
    1.82 +static bool
    1.83 +RequestMapInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
    1.84 +                     const void *key)
    1.85 +{
    1.86 +  RequestHashEntry *entry = static_cast<RequestHashEntry*>(hdr);
    1.87 +  entry->r = (void*)key;
    1.88 +  return true;
    1.89 +}
    1.90 +
    1.91 +static const PLDHashTableOps gMapOps = {
    1.92 +  PL_DHashAllocTable,
    1.93 +  PL_DHashFreeTable,
    1.94 +  PL_DHashVoidPtrKeyStub,
    1.95 +  RequestMapMatchEntry,
    1.96 +  PL_DHashMoveEntryStub,
    1.97 +  PL_DHashClearEntryStub,
    1.98 +  PL_DHashFinalizeStub,
    1.99 +  RequestMapInitEntry
   1.100 +};
   1.101 +
   1.102 +#ifdef DEBUG
   1.103 +class nsAutoAtomic {
   1.104 +  public:
   1.105 +    nsAutoAtomic(Atomic<int32_t> &i)
   1.106 +    :mI(i) {
   1.107 +      mI++;
   1.108 +    }
   1.109 +
   1.110 +    ~nsAutoAtomic() {
   1.111 +      mI--;
   1.112 +    }
   1.113 +
   1.114 +  protected:
   1.115 +    Atomic<int32_t> &mI;
   1.116 +
   1.117 +  private:
   1.118 +    nsAutoAtomic(); // not accessible
   1.119 +};
   1.120 +#endif
   1.121 +
   1.122 +nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
   1.123 +  : mReentrantMonitor("nsSecureBrowserUIImpl.mReentrantMonitor")
   1.124 +  , mNotifiedSecurityState(lis_no_security)
   1.125 +  , mNotifiedToplevelIsEV(false)
   1.126 +  , mNewToplevelSecurityState(STATE_IS_INSECURE)
   1.127 +  , mNewToplevelIsEV(false)
   1.128 +  , mNewToplevelSecurityStateKnown(true)
   1.129 +  , mIsViewSource(false)
   1.130 +  , mSubRequestsBrokenSecurity(0)
   1.131 +  , mSubRequestsNoSecurity(0)
   1.132 +  , mRestoreSubrequests(false)
   1.133 +  , mOnLocationChangeSeen(false)
   1.134 +#ifdef DEBUG
   1.135 +  , mOnStateLocationChangeReentranceDetection(0)
   1.136 +#endif
   1.137 +{
   1.138 +  mTransferringRequests.ops = nullptr;
   1.139 +  ResetStateTracking();
   1.140 +  
   1.141 +#if defined(PR_LOGGING)
   1.142 +  if (!gSecureDocLog)
   1.143 +    gSecureDocLog = PR_NewLogModule("nsSecureBrowserUI");
   1.144 +#endif /* PR_LOGGING */
   1.145 +}
   1.146 +
   1.147 +nsSecureBrowserUIImpl::~nsSecureBrowserUIImpl()
   1.148 +{
   1.149 +  if (mTransferringRequests.ops) {
   1.150 +    PL_DHashTableFinish(&mTransferringRequests);
   1.151 +    mTransferringRequests.ops = nullptr;
   1.152 +  }
   1.153 +}
   1.154 +
   1.155 +NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
   1.156 +                  nsISecureBrowserUI,
   1.157 +                  nsIWebProgressListener,
   1.158 +                  nsIFormSubmitObserver,
   1.159 +                  nsISupportsWeakReference,
   1.160 +                  nsISSLStatusProvider)
   1.161 +
   1.162 +NS_IMETHODIMP
   1.163 +nsSecureBrowserUIImpl::Init(nsIDOMWindow *aWindow)
   1.164 +{
   1.165 +
   1.166 +#ifdef PR_LOGGING
   1.167 +  nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
   1.168 +
   1.169 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.170 +         ("SecureUI:%p: Init: mWindow: %p, aWindow: %p\n", this,
   1.171 +          window.get(), aWindow));
   1.172 +#endif
   1.173 +
   1.174 +  if (!aWindow) {
   1.175 +    NS_WARNING("Null window passed to nsSecureBrowserUIImpl::Init()");
   1.176 +    return NS_ERROR_INVALID_ARG;
   1.177 +  }
   1.178 +
   1.179 +  if (mWindow) {
   1.180 +    NS_WARNING("Trying to init an nsSecureBrowserUIImpl twice");
   1.181 +    return NS_ERROR_ALREADY_INITIALIZED;
   1.182 +  }
   1.183 +
   1.184 +  nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(aWindow));
   1.185 +  if (pwin->IsInnerWindow()) {
   1.186 +    pwin = pwin->GetOuterWindow();
   1.187 +  }
   1.188 +
   1.189 +  nsresult rv;
   1.190 +  mWindow = do_GetWeakReference(pwin, &rv);
   1.191 +  NS_ENSURE_SUCCESS(rv, rv);
   1.192 +
   1.193 +  nsCOMPtr<nsPIDOMWindow> piwindow(do_QueryInterface(aWindow));
   1.194 +  if (!piwindow) return NS_ERROR_FAILURE;
   1.195 +
   1.196 +  nsIDocShell *docShell = piwindow->GetDocShell();
   1.197 +
   1.198 +  // The Docshell will own the SecureBrowserUI object
   1.199 +  if (!docShell)
   1.200 +    return NS_ERROR_FAILURE;
   1.201 +
   1.202 +  docShell->SetSecurityUI(this);
   1.203 +
   1.204 +  /* GetWebProgress(mWindow) */
   1.205 +  // hook up to the webprogress notifications.
   1.206 +  nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
   1.207 +  if (!wp) return NS_ERROR_FAILURE;
   1.208 +  /* end GetWebProgress */
   1.209 +  
   1.210 +  wp->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
   1.211 +                          nsIWebProgress::NOTIFY_STATE_ALL | 
   1.212 +                          nsIWebProgress::NOTIFY_LOCATION  |
   1.213 +                          nsIWebProgress::NOTIFY_SECURITY);
   1.214 +
   1.215 +
   1.216 +  return NS_OK;
   1.217 +}
   1.218 +
   1.219 +NS_IMETHODIMP
   1.220 +nsSecureBrowserUIImpl::GetState(uint32_t* aState)
   1.221 +{
   1.222 +  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.223 +  return MapInternalToExternalState(aState, mNotifiedSecurityState, mNotifiedToplevelIsEV);
   1.224 +}
   1.225 +
   1.226 +// static
   1.227 +already_AddRefed<nsISupports> 
   1.228 +nsSecureBrowserUIImpl::ExtractSecurityInfo(nsIRequest* aRequest)
   1.229 +{
   1.230 +  nsCOMPtr<nsISupports> retval;
   1.231 +  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   1.232 +  if (channel)
   1.233 +    channel->GetSecurityInfo(getter_AddRefs(retval));
   1.234 +  
   1.235 +  if (!retval) {
   1.236 +    nsCOMPtr<nsISecurityInfoProvider> provider(do_QueryInterface(aRequest));
   1.237 +    if (provider)
   1.238 +      provider->GetSecurityInfo(getter_AddRefs(retval));
   1.239 +  }
   1.240 +
   1.241 +  return retval.forget();
   1.242 +}
   1.243 +
   1.244 +nsresult
   1.245 +nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconState lock, bool ev)
   1.246 +{
   1.247 +  NS_ENSURE_ARG(aState);
   1.248 +
   1.249 +  switch (lock)
   1.250 +  {
   1.251 +    case lis_broken_security:
   1.252 +      *aState = STATE_IS_BROKEN;
   1.253 +      break;
   1.254 +
   1.255 +    case lis_mixed_security:
   1.256 +      *aState = STATE_IS_BROKEN;
   1.257 +      break;
   1.258 +
   1.259 +    case lis_high_security:
   1.260 +      *aState = STATE_IS_SECURE | STATE_SECURE_HIGH;
   1.261 +      break;
   1.262 +
   1.263 +    default:
   1.264 +    case lis_no_security:
   1.265 +      *aState = STATE_IS_INSECURE;
   1.266 +      break;
   1.267 +  }
   1.268 +
   1.269 +  if (ev && (*aState & STATE_IS_SECURE))
   1.270 +    *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
   1.271 +
   1.272 +  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
   1.273 +  if (!docShell)
   1.274 +    return NS_OK;
   1.275 +
   1.276 +  // For content docShell's, the mixed content security state is set on the root docShell.
   1.277 +  if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
   1.278 +    nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(docShell));
   1.279 +    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
   1.280 +    docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
   1.281 +    NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
   1.282 +    docShell = do_QueryInterface(sameTypeRoot);
   1.283 +    if (!docShell)
   1.284 +      return NS_OK;
   1.285 +  }
   1.286 +
   1.287 +  // Has a Mixed Content Load initiated in nsMixedContentBlocker?
   1.288 +  // If so, the state should be broken; overriding the previous state
   1.289 +  // set by the lock parameter.
   1.290 +  if (docShell->GetHasMixedActiveContentLoaded() &&
   1.291 +      docShell->GetHasMixedDisplayContentLoaded()) {
   1.292 +      *aState = STATE_IS_BROKEN |
   1.293 +                nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
   1.294 +                nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
   1.295 +  } else if (docShell->GetHasMixedActiveContentLoaded()) {
   1.296 +      *aState = STATE_IS_BROKEN |
   1.297 +                nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
   1.298 +  } else if (docShell->GetHasMixedDisplayContentLoaded()) {
   1.299 +      *aState = STATE_IS_BROKEN |
   1.300 +                nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
   1.301 +  }
   1.302 +
   1.303 +  // Has Mixed Content Been Blocked in nsMixedContentBlocker?
   1.304 +  if (docShell->GetHasMixedActiveContentBlocked())
   1.305 +    *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
   1.306 +
   1.307 +  if (docShell->GetHasMixedDisplayContentBlocked())
   1.308 +    *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
   1.309 +
   1.310 +  return NS_OK;
   1.311 +}
   1.312 +
   1.313 +NS_IMETHODIMP
   1.314 +nsSecureBrowserUIImpl::SetDocShell(nsIDocShell *aDocShell)
   1.315 +{
   1.316 +  nsresult rv;
   1.317 +  mDocShell = do_GetWeakReference(aDocShell, &rv);
   1.318 +  return rv;
   1.319 +}
   1.320 +
   1.321 +static nsresult IsChildOfDomWindow(nsIDOMWindow *parent, nsIDOMWindow *child,
   1.322 +                                   bool* value)
   1.323 +{
   1.324 +  *value = false;
   1.325 +  
   1.326 +  if (parent == child) {
   1.327 +    *value = true;
   1.328 +    return NS_OK;
   1.329 +  }
   1.330 +  
   1.331 +  nsCOMPtr<nsIDOMWindow> childsParent;
   1.332 +  child->GetParent(getter_AddRefs(childsParent));
   1.333 +  
   1.334 +  if (childsParent && childsParent.get() != child)
   1.335 +    IsChildOfDomWindow(parent, childsParent, value);
   1.336 +  
   1.337 +  return NS_OK;
   1.338 +}
   1.339 +
   1.340 +static uint32_t GetSecurityStateFromSecurityInfo(nsISupports *info)
   1.341 +{
   1.342 +  nsresult res;
   1.343 +  uint32_t securityState;
   1.344 +
   1.345 +  nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
   1.346 +  if (!psmInfo) {
   1.347 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - no nsITransportSecurityInfo for %p\n",
   1.348 +                                         (nsISupports *)info));
   1.349 +    return nsIWebProgressListener::STATE_IS_INSECURE;
   1.350 +  }
   1.351 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - info is %p\n", 
   1.352 +                                       (nsISupports *)info));
   1.353 +  
   1.354 +  res = psmInfo->GetSecurityState(&securityState);
   1.355 +  if (NS_FAILED(res)) {
   1.356 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - GetSecurityState failed: %d\n",
   1.357 +                                         res));
   1.358 +    securityState = nsIWebProgressListener::STATE_IS_BROKEN;
   1.359 +  }
   1.360 +  
   1.361 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - Returning %d\n", 
   1.362 +                                       securityState));
   1.363 +  return securityState;
   1.364 +}
   1.365 +
   1.366 +
   1.367 +NS_IMETHODIMP
   1.368 +nsSecureBrowserUIImpl::Notify(nsIDOMHTMLFormElement* aDOMForm,
   1.369 +                              nsIDOMWindow* aWindow, nsIURI* actionURL,
   1.370 +                              bool* cancelSubmit)
   1.371 +{
   1.372 +  // Return NS_OK unless we want to prevent this form from submitting.
   1.373 +  *cancelSubmit = false;
   1.374 +  if (!aWindow || !actionURL || !aDOMForm)
   1.375 +    return NS_OK;
   1.376 +  
   1.377 +  nsCOMPtr<nsIContent> formNode = do_QueryInterface(aDOMForm);
   1.378 +
   1.379 +  nsCOMPtr<nsIDocument> document = formNode->GetDocument();
   1.380 +  if (!document) return NS_OK;
   1.381 +
   1.382 +  nsIPrincipal *principal = formNode->NodePrincipal();
   1.383 +  
   1.384 +  if (!principal)
   1.385 +  {
   1.386 +    *cancelSubmit = true;
   1.387 +    return NS_OK;
   1.388 +  }
   1.389 +
   1.390 +  nsCOMPtr<nsIURI> formURL;
   1.391 +  if (NS_FAILED(principal->GetURI(getter_AddRefs(formURL))) ||
   1.392 +      !formURL)
   1.393 +  {
   1.394 +    formURL = document->GetDocumentURI();
   1.395 +  }
   1.396 +
   1.397 +  nsCOMPtr<nsIDOMWindow> postingWindow =
   1.398 +    do_QueryInterface(document->GetWindow());
   1.399 +  // We can't find this document's window, cancel it.
   1.400 +  if (!postingWindow)
   1.401 +  {
   1.402 +    NS_WARNING("If you see this and can explain why it should be allowed, note in Bug 332324");
   1.403 +    *cancelSubmit = true;
   1.404 +    return NS_OK;
   1.405 +  }
   1.406 +
   1.407 +  nsCOMPtr<nsIDOMWindow> window;
   1.408 +  {
   1.409 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.410 +    window = do_QueryReferent(mWindow);
   1.411 +
   1.412 +    // The window was destroyed, so we assume no form was submitted within it.
   1.413 +    if (!window)
   1.414 +      return NS_OK;
   1.415 +  }
   1.416 +
   1.417 +  bool isChild;
   1.418 +  IsChildOfDomWindow(window, postingWindow, &isChild);
   1.419 +  
   1.420 +  // This notify call is not for our window, ignore it.
   1.421 +  if (!isChild)
   1.422 +    return NS_OK;
   1.423 +  
   1.424 +  bool okayToPost;
   1.425 +  nsresult res = CheckPost(formURL, actionURL, &okayToPost);
   1.426 +  
   1.427 +  if (NS_SUCCEEDED(res) && !okayToPost)
   1.428 +    *cancelSubmit = true;
   1.429 +  
   1.430 +  return res;
   1.431 +}
   1.432 +
   1.433 +//  nsIWebProgressListener
   1.434 +NS_IMETHODIMP 
   1.435 +nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress* aWebProgress,
   1.436 +                                        nsIRequest* aRequest,
   1.437 +                                        int32_t aCurSelfProgress,
   1.438 +                                        int32_t aMaxSelfProgress,
   1.439 +                                        int32_t aCurTotalProgress,
   1.440 +                                        int32_t aMaxTotalProgress)
   1.441 +{
   1.442 +  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   1.443 +  return NS_OK;
   1.444 +}
   1.445 +
   1.446 +void nsSecureBrowserUIImpl::ResetStateTracking()
   1.447 +{
   1.448 +  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.449 +
   1.450 +  mDocumentRequestsInProgress = 0;
   1.451 +  if (mTransferringRequests.ops) {
   1.452 +    PL_DHashTableFinish(&mTransferringRequests);
   1.453 +    mTransferringRequests.ops = nullptr;
   1.454 +  }
   1.455 +  PL_DHashTableInit(&mTransferringRequests, &gMapOps, nullptr,
   1.456 +                    sizeof(RequestHashEntry), 16);
   1.457 +}
   1.458 +
   1.459 +nsresult
   1.460 +nsSecureBrowserUIImpl::EvaluateAndUpdateSecurityState(nsIRequest* aRequest, nsISupports *info,
   1.461 +                                                      bool withNewLocation)
   1.462 +{
   1.463 +  /* I explicitly ignore the camelCase variable naming style here,
   1.464 +     I want to make it clear these are temp variables that relate to the 
   1.465 +     member variables with the same suffix.*/
   1.466 +
   1.467 +  uint32_t temp_NewToplevelSecurityState = nsIWebProgressListener::STATE_IS_INSECURE;
   1.468 +  bool temp_NewToplevelIsEV = false;
   1.469 +
   1.470 +  bool updateStatus = false;
   1.471 +  nsCOMPtr<nsISSLStatus> temp_SSLStatus;
   1.472 +
   1.473 +    temp_NewToplevelSecurityState = GetSecurityStateFromSecurityInfo(info);
   1.474 +
   1.475 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.476 +           ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n", this,
   1.477 +            temp_NewToplevelSecurityState));
   1.478 +
   1.479 +    nsCOMPtr<nsISSLStatusProvider> sp = do_QueryInterface(info);
   1.480 +    if (sp) {
   1.481 +      // Ignore result
   1.482 +      updateStatus = true;
   1.483 +      (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
   1.484 +      if (temp_SSLStatus) {
   1.485 +        bool aTemp;
   1.486 +        if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) {
   1.487 +          temp_NewToplevelIsEV = aTemp;
   1.488 +        }
   1.489 +      }
   1.490 +    }
   1.491 +
   1.492 +  // assume temp_NewToplevelSecurityState was set in this scope!
   1.493 +  // see code that is directly above
   1.494 +
   1.495 +  {
   1.496 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.497 +    mNewToplevelSecurityStateKnown = true;
   1.498 +    mNewToplevelSecurityState = temp_NewToplevelSecurityState;
   1.499 +    mNewToplevelIsEV = temp_NewToplevelIsEV;
   1.500 +    if (updateStatus) {
   1.501 +      mSSLStatus = temp_SSLStatus;
   1.502 +    }
   1.503 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.504 +           ("SecureUI:%p: remember securityInfo %p\n", this,
   1.505 +            info));
   1.506 +    nsCOMPtr<nsIAssociatedContentSecurity> associatedContentSecurityFromRequest =
   1.507 +        do_QueryInterface(aRequest);
   1.508 +    if (associatedContentSecurityFromRequest)
   1.509 +        mCurrentToplevelSecurityInfo = aRequest;
   1.510 +    else
   1.511 +        mCurrentToplevelSecurityInfo = info;
   1.512 +
   1.513 +    // The subrequest counters are now in sync with 
   1.514 +    // mCurrentToplevelSecurityInfo, don't restore after top level
   1.515 +    // document load finishes.
   1.516 +    mRestoreSubrequests = false;
   1.517 +  }
   1.518 +
   1.519 +  return UpdateSecurityState(aRequest, withNewLocation, updateStatus);
   1.520 +}
   1.521 +
   1.522 +void
   1.523 +nsSecureBrowserUIImpl::UpdateSubrequestMembers(nsISupports *securityInfo)
   1.524 +{
   1.525 +  // For wyciwyg channels in subdocuments we only update our
   1.526 +  // subrequest state members.
   1.527 +  uint32_t reqState = GetSecurityStateFromSecurityInfo(securityInfo);
   1.528 +
   1.529 +  // the code above this line should run without a lock
   1.530 +  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.531 +
   1.532 +  if (reqState & STATE_IS_SECURE) {
   1.533 +    // do nothing
   1.534 +  } else if (reqState & STATE_IS_BROKEN) {
   1.535 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.536 +           ("SecureUI:%p: OnStateChange: subreq BROKEN\n", this));
   1.537 +    ++mSubRequestsBrokenSecurity;
   1.538 +  } else {
   1.539 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.540 +           ("SecureUI:%p: OnStateChange: subreq INSECURE\n", this));
   1.541 +    ++mSubRequestsNoSecurity;
   1.542 +  }
   1.543 +}
   1.544 +
   1.545 +NS_IMETHODIMP
   1.546 +nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
   1.547 +                                     nsIRequest* aRequest,
   1.548 +                                     uint32_t aProgressStateFlags,
   1.549 +                                     nsresult aStatus)
   1.550 +{
   1.551 +#ifdef DEBUG
   1.552 +  nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
   1.553 +  NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
   1.554 +               "unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
   1.555 +#endif
   1.556 +  /*
   1.557 +    All discussion, unless otherwise mentioned, only refers to
   1.558 +    http, https, file or wyciwig requests.
   1.559 +
   1.560 +
   1.561 +    Redirects are evil, well, some of them.
   1.562 +    There are multiple forms of redirects.
   1.563 +
   1.564 +    Redirects caused by http refresh content are ok, because experiments show,
   1.565 +    with those redirects, the old page contents and their requests will come to STOP
   1.566 +    completely, before any progress from new refreshed page content is reported.
   1.567 +    So we can safely treat them as separate page loading transactions.
   1.568 +
   1.569 +    Evil are redirects at the http protocol level, like code 302.
   1.570 +
   1.571 +    If the toplevel documents gets replaced, i.e. redirected with 302, we do not care for the 
   1.572 +    security state of the initial transaction, which has now been redirected, 
   1.573 +    we only care for the new page load.
   1.574 +    
   1.575 +    For the implementation of the security UI, we make an assumption, that is hopefully true.
   1.576 +    
   1.577 +    Imagine, the received page that was delivered with the 302 redirection answer,
   1.578 +    also delivered html content.
   1.579 +
   1.580 +    What happens if the parser starts to analyze the content and tries to load contained sub objects?
   1.581 +    
   1.582 +    In that case we would see start and stop requests for subdocuments, some for the previous document,
   1.583 +    some for the new target document. And only those for the new toplevel document may be
   1.584 +    taken into consideration, when deciding about the security state of the next toplevel document.
   1.585 +    
   1.586 +    Because security state is being looked at, when loading stops for (sub)documents, this 
   1.587 +    could cause real confusion, because we have to decide, whether an incoming progress 
   1.588 +    belongs to the new toplevel page, or the previous, already redirected page.
   1.589 +    
   1.590 +    Can we simplify here?
   1.591 +    
   1.592 +    If a redirect at the http protocol level is seen, can we safely assume, its html content
   1.593 +    will not be parsed, anylzed, and no embedded objects will get loaded (css, js, images),
   1.594 +    because the redirect is already happening?
   1.595 +    
   1.596 +    If we can assume that, this really simplify things. Because we will never see notification
   1.597 +    for sub requests that need to get ignored.
   1.598 +    
   1.599 +    I would like to make this assumption for now, but please let me (kaie) know if I'm wrong.
   1.600 +    
   1.601 +    Excurse:
   1.602 +      If my assumption is wrong, then we would require more tracking information.
   1.603 +      We need to keep lists of all pointers to request object that had been seen since the
   1.604 +      last toplevel start event.
   1.605 +      If the start for a redirected page is seen, the list of releveant object must be cleared,
   1.606 +      and only progress for requests which start after it must be analyzed.
   1.607 +      All other events must be ignored, as they belong to now irrelevant previous top level documents.
   1.608 +
   1.609 +
   1.610 +    Frames are also evil.
   1.611 +
   1.612 +    First we need a decision.
   1.613 +    kaie thinks: 
   1.614 +      Only if the toplevel frame is secure, we should try to display secure lock icons.
   1.615 +      If some of the inner contents are insecure, we display mixed mode.
   1.616 +      
   1.617 +      But if the top level frame is not secure, why indicate a mixed lock icon at all?
   1.618 +      I think we should always display an open lock icon, if the top level frameset is insecure.
   1.619 +      
   1.620 +      That's the way Netscape Communicator behaves, and I think we should do the same.
   1.621 +      
   1.622 +      The user will not know which parts are secure and which are not,
   1.623 +      and any certificate information, displayed in the tooltip or in the "page info"
   1.624 +      will only be relevant for some subframe(s), and the user will not know which ones,
   1.625 +      so we shouldn't display it as a general attribute of the displayed page.
   1.626 +
   1.627 +    Why are frames evil?
   1.628 +    
   1.629 +    Because the progress for the toplevel frame document is not easily distinguishable
   1.630 +    from subframes. The same STATE bits are reported.
   1.631 +
   1.632 +    While at first sight, when a new page load happens,
   1.633 +    the toplevel frameset document has also the STATE_IS_NETWORK bit in it.
   1.634 +    But this can't really be used. Because in case that document causes a http 302 redirect, 
   1.635 +    the real top level frameset will no longer have that bit.
   1.636 +    
   1.637 +    But we need some way to distinguish top level frames from inner frames.
   1.638 +    
   1.639 +    I saw that the web progress we get delivered has a reference to the toplevel DOM window.
   1.640 +    
   1.641 +    I suggest, we look at all incoming requests.
   1.642 +    If a request is NOT for the toplevel DOM window, we will always treat it as a subdocument request,
   1.643 +    regardless of whether the load flags indicate a top level document.
   1.644 +  */
   1.645 +
   1.646 +  nsCOMPtr<nsIDOMWindow> windowForProgress;
   1.647 +  aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
   1.648 +
   1.649 +  nsCOMPtr<nsIDOMWindow> window;
   1.650 +  bool isViewSource;
   1.651 +
   1.652 +  nsCOMPtr<nsINetUtil> ioService;
   1.653 +
   1.654 +  {
   1.655 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.656 +    window = do_QueryReferent(mWindow);
   1.657 +    NS_ASSERTION(window, "Window has gone away?!");
   1.658 +    isViewSource = mIsViewSource;
   1.659 +    ioService = mIOService;
   1.660 +  }
   1.661 +
   1.662 +  if (!ioService)
   1.663 +  {
   1.664 +    ioService = do_GetService(NS_IOSERVICE_CONTRACTID);
   1.665 +    if (ioService)
   1.666 +    {
   1.667 +      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.668 +      mIOService = ioService;
   1.669 +    }
   1.670 +  }
   1.671 +
   1.672 +  bool isNoContentResponse = false;
   1.673 +  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   1.674 +  if (httpChannel) 
   1.675 +  {
   1.676 +    uint32_t response;
   1.677 +    isNoContentResponse = NS_SUCCEEDED(httpChannel->GetResponseStatus(&response)) &&
   1.678 +        (response == 204 || response == 205);
   1.679 +  }
   1.680 +  const bool isToplevelProgress = (windowForProgress.get() == window.get()) && !isNoContentResponse;
   1.681 +  
   1.682 +#ifdef PR_LOGGING
   1.683 +  if (windowForProgress)
   1.684 +  {
   1.685 +    if (isToplevelProgress)
   1.686 +    {
   1.687 +      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.688 +             ("SecureUI:%p: OnStateChange: progress: for toplevel\n", this));
   1.689 +    }
   1.690 +    else
   1.691 +    {
   1.692 +      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.693 +             ("SecureUI:%p: OnStateChange: progress: for something else\n", this));
   1.694 +    }
   1.695 +  }
   1.696 +  else
   1.697 +  {
   1.698 +      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.699 +             ("SecureUI:%p: OnStateChange: progress: no window known\n", this));
   1.700 +  }
   1.701 +#endif
   1.702 +
   1.703 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.704 +         ("SecureUI:%p: OnStateChange\n", this));
   1.705 +
   1.706 +  if (isViewSource)
   1.707 +    return NS_OK;
   1.708 +
   1.709 +  if (!aRequest)
   1.710 +  {
   1.711 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.712 +           ("SecureUI:%p: OnStateChange with null request\n", this));
   1.713 +    return NS_ERROR_NULL_POINTER;
   1.714 +  }
   1.715 +
   1.716 +#ifdef PR_LOGGING
   1.717 +  if (PR_LOG_TEST(gSecureDocLog, PR_LOG_DEBUG)) {
   1.718 +    nsXPIDLCString reqname;
   1.719 +    aRequest->GetName(reqname);
   1.720 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.721 +           ("SecureUI:%p: %p %p OnStateChange %x %s\n", this, aWebProgress,
   1.722 +            aRequest, aProgressStateFlags, reqname.get()));
   1.723 +  }
   1.724 +#endif
   1.725 +
   1.726 +  nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
   1.727 +
   1.728 +  nsCOMPtr<nsIURI> uri;
   1.729 +  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   1.730 +  if (channel) {
   1.731 +    channel->GetURI(getter_AddRefs(uri));
   1.732 +  }
   1.733 +
   1.734 +  nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(aRequest));
   1.735 +  if (imgRequest) {
   1.736 +    NS_ASSERTION(!channel, "How did that happen, exactly?");
   1.737 +    // for image requests, we get the URI from here
   1.738 +    imgRequest->GetURI(getter_AddRefs(uri));
   1.739 +  }
   1.740 +  
   1.741 +  if (uri) {
   1.742 +    bool vs;
   1.743 +    if (NS_SUCCEEDED(uri->SchemeIs("javascript", &vs)) && vs) {
   1.744 +      // We ignore the progress events for javascript URLs.
   1.745 +      // If a document loading gets triggered, we will see more events.
   1.746 +      return NS_OK;
   1.747 +    }
   1.748 +  }
   1.749 +
   1.750 +  uint32_t loadFlags = 0;
   1.751 +  aRequest->GetLoadFlags(&loadFlags);
   1.752 +
   1.753 +#ifdef PR_LOGGING
   1.754 +  if (aProgressStateFlags & STATE_START
   1.755 +      &&
   1.756 +      aProgressStateFlags & STATE_IS_REQUEST
   1.757 +      &&
   1.758 +      isToplevelProgress
   1.759 +      &&
   1.760 +      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
   1.761 +  {
   1.762 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.763 +           ("SecureUI:%p: OnStateChange: SOMETHING STARTS FOR TOPMOST DOCUMENT\n", this));
   1.764 +  }
   1.765 +
   1.766 +  if (aProgressStateFlags & STATE_STOP
   1.767 +      &&
   1.768 +      aProgressStateFlags & STATE_IS_REQUEST
   1.769 +      &&
   1.770 +      isToplevelProgress
   1.771 +      &&
   1.772 +      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
   1.773 +  {
   1.774 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.775 +           ("SecureUI:%p: OnStateChange: SOMETHING STOPS FOR TOPMOST DOCUMENT\n", this));
   1.776 +  }
   1.777 +#endif
   1.778 +
   1.779 +  bool isSubDocumentRelevant = true;
   1.780 +
   1.781 +  // We are only interested in requests that load in the browser window...
   1.782 +  if (!imgRequest) { // is not imgRequest
   1.783 +    nsCOMPtr<nsIHttpChannel> httpRequest(do_QueryInterface(aRequest));
   1.784 +    if (!httpRequest) {
   1.785 +      nsCOMPtr<nsIFileChannel> fileRequest(do_QueryInterface(aRequest));
   1.786 +      if (!fileRequest) {
   1.787 +        nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
   1.788 +        if (!wyciwygRequest) {
   1.789 +          nsCOMPtr<nsIFTPChannel> ftpRequest(do_QueryInterface(aRequest));
   1.790 +          if (!ftpRequest) {
   1.791 +            PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.792 +                   ("SecureUI:%p: OnStateChange: not relevant for sub content\n", this));
   1.793 +            isSubDocumentRelevant = false;
   1.794 +          }
   1.795 +        }
   1.796 +      }
   1.797 +    }
   1.798 +  }
   1.799 +
   1.800 +  // This will ignore all resource, chrome, data, file, moz-icon, and anno
   1.801 +  // protocols. Local resources are treated as trusted.
   1.802 +  if (uri && ioService) {
   1.803 +    bool hasFlag;
   1.804 +    nsresult rv = 
   1.805 +      ioService->URIChainHasFlags(uri, 
   1.806 +                                  nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
   1.807 +                                  &hasFlag);
   1.808 +    if (NS_SUCCEEDED(rv) && hasFlag) {
   1.809 +      isSubDocumentRelevant = false;
   1.810 +    }
   1.811 +  }
   1.812 +
   1.813 +#if defined(DEBUG)
   1.814 +  nsCString info2;
   1.815 +  uint32_t testFlags = loadFlags;
   1.816 +
   1.817 +  if (testFlags & nsIChannel::LOAD_DOCUMENT_URI)
   1.818 +  {
   1.819 +    testFlags -= nsIChannel::LOAD_DOCUMENT_URI;
   1.820 +    info2.Append("LOAD_DOCUMENT_URI ");
   1.821 +  }
   1.822 +  if (testFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
   1.823 +  {
   1.824 +    testFlags -= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
   1.825 +    info2.Append("LOAD_RETARGETED_DOCUMENT_URI ");
   1.826 +  }
   1.827 +  if (testFlags & nsIChannel::LOAD_REPLACE)
   1.828 +  {
   1.829 +    testFlags -= nsIChannel::LOAD_REPLACE;
   1.830 +    info2.Append("LOAD_REPLACE ");
   1.831 +  }
   1.832 +
   1.833 +  const char *_status = NS_SUCCEEDED(aStatus) ? "1" : "0";
   1.834 +
   1.835 +  nsCString info;
   1.836 +  uint32_t f = aProgressStateFlags;
   1.837 +  if (f & nsIWebProgressListener::STATE_START)
   1.838 +  {
   1.839 +    f -= nsIWebProgressListener::STATE_START;
   1.840 +    info.Append("START ");
   1.841 +  }
   1.842 +  if (f & nsIWebProgressListener::STATE_REDIRECTING)
   1.843 +  {
   1.844 +    f -= nsIWebProgressListener::STATE_REDIRECTING;
   1.845 +    info.Append("REDIRECTING ");
   1.846 +  }
   1.847 +  if (f & nsIWebProgressListener::STATE_TRANSFERRING)
   1.848 +  {
   1.849 +    f -= nsIWebProgressListener::STATE_TRANSFERRING;
   1.850 +    info.Append("TRANSFERRING ");
   1.851 +  }
   1.852 +  if (f & nsIWebProgressListener::STATE_NEGOTIATING)
   1.853 +  {
   1.854 +    f -= nsIWebProgressListener::STATE_NEGOTIATING;
   1.855 +    info.Append("NEGOTIATING ");
   1.856 +  }
   1.857 +  if (f & nsIWebProgressListener::STATE_STOP)
   1.858 +  {
   1.859 +    f -= nsIWebProgressListener::STATE_STOP;
   1.860 +    info.Append("STOP ");
   1.861 +  }
   1.862 +  if (f & nsIWebProgressListener::STATE_IS_REQUEST)
   1.863 +  {
   1.864 +    f -= nsIWebProgressListener::STATE_IS_REQUEST;
   1.865 +    info.Append("IS_REQUEST ");
   1.866 +  }
   1.867 +  if (f & nsIWebProgressListener::STATE_IS_DOCUMENT)
   1.868 +  {
   1.869 +    f -= nsIWebProgressListener::STATE_IS_DOCUMENT;
   1.870 +    info.Append("IS_DOCUMENT ");
   1.871 +  }
   1.872 +  if (f & nsIWebProgressListener::STATE_IS_NETWORK)
   1.873 +  {
   1.874 +    f -= nsIWebProgressListener::STATE_IS_NETWORK;
   1.875 +    info.Append("IS_NETWORK ");
   1.876 +  }
   1.877 +  if (f & nsIWebProgressListener::STATE_IS_WINDOW)
   1.878 +  {
   1.879 +    f -= nsIWebProgressListener::STATE_IS_WINDOW;
   1.880 +    info.Append("IS_WINDOW ");
   1.881 +  }
   1.882 +  if (f & nsIWebProgressListener::STATE_IS_INSECURE)
   1.883 +  {
   1.884 +    f -= nsIWebProgressListener::STATE_IS_INSECURE;
   1.885 +    info.Append("IS_INSECURE ");
   1.886 +  }
   1.887 +  if (f & nsIWebProgressListener::STATE_IS_BROKEN)
   1.888 +  {
   1.889 +    f -= nsIWebProgressListener::STATE_IS_BROKEN;
   1.890 +    info.Append("IS_BROKEN ");
   1.891 +  }
   1.892 +  if (f & nsIWebProgressListener::STATE_IS_SECURE)
   1.893 +  {
   1.894 +    f -= nsIWebProgressListener::STATE_IS_SECURE;
   1.895 +    info.Append("IS_SECURE ");
   1.896 +  }
   1.897 +  if (f & nsIWebProgressListener::STATE_SECURE_HIGH)
   1.898 +  {
   1.899 +    f -= nsIWebProgressListener::STATE_SECURE_HIGH;
   1.900 +    info.Append("SECURE_HIGH ");
   1.901 +  }
   1.902 +  if (f & nsIWebProgressListener::STATE_RESTORING)
   1.903 +  {
   1.904 +    f -= nsIWebProgressListener::STATE_RESTORING;
   1.905 +    info.Append("STATE_RESTORING ");
   1.906 +  }
   1.907 +
   1.908 +  if (f > 0)
   1.909 +  {
   1.910 +    info.Append("f contains unknown flag!");
   1.911 +  }
   1.912 +
   1.913 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.914 +         ("SecureUI:%p: OnStateChange: %s %s -- %s\n", this, _status, 
   1.915 +          info.get(), info2.get()));
   1.916 +
   1.917 +  if (aProgressStateFlags & STATE_STOP
   1.918 +      &&
   1.919 +      channel)
   1.920 +  {
   1.921 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   1.922 +           ("SecureUI:%p: OnStateChange: seeing STOP with security state: %d\n", this,
   1.923 +            GetSecurityStateFromSecurityInfo(securityInfo)
   1.924 +            ));
   1.925 +  }
   1.926 +#endif
   1.927 +
   1.928 +  if (aProgressStateFlags & STATE_TRANSFERRING
   1.929 +      &&
   1.930 +      aProgressStateFlags & STATE_IS_REQUEST)
   1.931 +  {
   1.932 +    // The listing of a request in mTransferringRequests
   1.933 +    // means, there has already been data transfered.
   1.934 +
   1.935 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.936 +    PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_ADD);
   1.937 +    
   1.938 +    return NS_OK;
   1.939 +  }
   1.940 +
   1.941 +  bool requestHasTransferedData = false;
   1.942 +
   1.943 +  if (aProgressStateFlags & STATE_STOP
   1.944 +      &&
   1.945 +      aProgressStateFlags & STATE_IS_REQUEST)
   1.946 +  {
   1.947 +    { /* scope for the ReentrantMonitorAutoEnter */
   1.948 +      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   1.949 +      PLDHashEntryHdr *entry = PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_LOOKUP);
   1.950 +      if (PL_DHASH_ENTRY_IS_BUSY(entry))
   1.951 +      {
   1.952 +        PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_REMOVE);
   1.953 +
   1.954 +        requestHasTransferedData = true;
   1.955 +      }
   1.956 +    }
   1.957 +
   1.958 +    if (!requestHasTransferedData) {
   1.959 +      // Because image loads doesn't support any TRANSFERRING notifications but
   1.960 +      // only START and STOP we must ask them directly whether content was
   1.961 +      // transferred.  See bug 432685 for details.
   1.962 +      nsCOMPtr<nsISecurityInfoProvider> securityInfoProvider =
   1.963 +        do_QueryInterface(aRequest);
   1.964 +      // Guess true in all failure cases to be safe.  But if we're not
   1.965 +      // an nsISecurityInfoProvider, then we just haven't transferred
   1.966 +      // any data.
   1.967 +      bool hasTransferred;
   1.968 +      requestHasTransferedData =
   1.969 +        securityInfoProvider &&
   1.970 +        (NS_FAILED(securityInfoProvider->GetHasTransferredData(&hasTransferred)) ||
   1.971 +         hasTransferred);
   1.972 +    }
   1.973 +  }
   1.974 +
   1.975 +  bool allowSecurityStateChange = true;
   1.976 +  if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
   1.977 +  {
   1.978 +    // The original consumer (this) is no longer the target of the load.
   1.979 +    // Ignore any events with this flag, do not allow them to update
   1.980 +    // our secure UI state.
   1.981 +    allowSecurityStateChange = false;
   1.982 +  }
   1.983 +
   1.984 +  if (aProgressStateFlags & STATE_START
   1.985 +      &&
   1.986 +      aProgressStateFlags & STATE_IS_REQUEST
   1.987 +      &&
   1.988 +      isToplevelProgress
   1.989 +      &&
   1.990 +      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
   1.991 +  {
   1.992 +    bool inProgress;
   1.993 +
   1.994 +    int32_t saveSubBroken;
   1.995 +    int32_t saveSubNo;
   1.996 +    nsCOMPtr<nsIAssociatedContentSecurity> prevContentSecurity;
   1.997 +
   1.998 +    int32_t newSubBroken = 0;
   1.999 +    int32_t newSubNo = 0;
  1.1000 +
  1.1001 +    {
  1.1002 +      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1003 +      inProgress = (mDocumentRequestsInProgress!=0);
  1.1004 +
  1.1005 +      if (allowSecurityStateChange && !inProgress)
  1.1006 +      {
  1.1007 +        saveSubBroken = mSubRequestsBrokenSecurity;
  1.1008 +        saveSubNo = mSubRequestsNoSecurity;
  1.1009 +        prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
  1.1010 +      }
  1.1011 +    }
  1.1012 +
  1.1013 +    if (allowSecurityStateChange && !inProgress)
  1.1014 +    {
  1.1015 +      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1016 +             ("SecureUI:%p: OnStateChange: start for toplevel document\n", this
  1.1017 +              ));
  1.1018 +
  1.1019 +      if (prevContentSecurity)
  1.1020 +      {
  1.1021 +        PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1022 +               ("SecureUI:%p: OnStateChange: start, saving current sub state\n", this
  1.1023 +                ));
  1.1024 +  
  1.1025 +        // before resetting our state, let's save information about
  1.1026 +        // sub element loads, so we can restore it later
  1.1027 +        prevContentSecurity->SetCountSubRequestsBrokenSecurity(saveSubBroken);
  1.1028 +        prevContentSecurity->SetCountSubRequestsNoSecurity(saveSubNo);
  1.1029 +        prevContentSecurity->Flush();
  1.1030 +        PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI:%p: Saving subs in START to %p as %d,%d\n", 
  1.1031 +          this, prevContentSecurity.get(), saveSubBroken, saveSubNo));      
  1.1032 +      }
  1.1033 +
  1.1034 +      bool retrieveAssociatedState = false;
  1.1035 +
  1.1036 +      if (securityInfo &&
  1.1037 +          (aProgressStateFlags & nsIWebProgressListener::STATE_RESTORING) != 0) {
  1.1038 +        retrieveAssociatedState = true;
  1.1039 +      } else {
  1.1040 +        nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
  1.1041 +        if (wyciwygRequest) {
  1.1042 +          retrieveAssociatedState = true;
  1.1043 +        }
  1.1044 +      }
  1.1045 +
  1.1046 +      if (retrieveAssociatedState)
  1.1047 +      {
  1.1048 +        // When restoring from bfcache, we will not get events for the 
  1.1049 +        // page's sub elements, so let's load the state of sub elements
  1.1050 +        // from the cache.
  1.1051 +    
  1.1052 +        nsCOMPtr<nsIAssociatedContentSecurity> 
  1.1053 +          newContentSecurity(do_QueryInterface(securityInfo));
  1.1054 +    
  1.1055 +        if (newContentSecurity)
  1.1056 +        {
  1.1057 +          PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1058 +                 ("SecureUI:%p: OnStateChange: start, loading old sub state\n", this
  1.1059 +                  ));
  1.1060 +    
  1.1061 +          newContentSecurity->GetCountSubRequestsBrokenSecurity(&newSubBroken);
  1.1062 +          newContentSecurity->GetCountSubRequestsNoSecurity(&newSubNo);
  1.1063 +          PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI:%p: Restoring subs in START from %p to %d,%d\n", 
  1.1064 +            this, newContentSecurity.get(), newSubBroken, newSubNo));      
  1.1065 +        }
  1.1066 +      }
  1.1067 +      else
  1.1068 +      {
  1.1069 +        // If we don't get OnLocationChange for this top level load later,
  1.1070 +        // it didn't get rendered.  But we reset the state to unknown and
  1.1071 +        // mSubRequests* to zeros.  If we would have left these values after 
  1.1072 +        // this top level load stoped, we would override the original top level
  1.1073 +        // load with all zeros and break mixed content state on back and forward.
  1.1074 +        mRestoreSubrequests = true;
  1.1075 +      }
  1.1076 +    }
  1.1077 +
  1.1078 +    {
  1.1079 +      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1080 +
  1.1081 +      if (allowSecurityStateChange && !inProgress)
  1.1082 +      {
  1.1083 +        ResetStateTracking();
  1.1084 +        mSubRequestsBrokenSecurity = newSubBroken;
  1.1085 +        mSubRequestsNoSecurity = newSubNo;
  1.1086 +        mNewToplevelSecurityStateKnown = false;
  1.1087 +      }
  1.1088 +
  1.1089 +      // By using a counter, this code also works when the toplevel
  1.1090 +      // document get's redirected, but the STOP request for the 
  1.1091 +      // previous toplevel document has not yet have been received.
  1.1092 +      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1093 +             ("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this
  1.1094 +              ));
  1.1095 +      ++mDocumentRequestsInProgress;
  1.1096 +    }
  1.1097 +
  1.1098 +    return NS_OK;
  1.1099 +  }
  1.1100 +
  1.1101 +  if (aProgressStateFlags & STATE_STOP
  1.1102 +      &&
  1.1103 +      aProgressStateFlags & STATE_IS_REQUEST
  1.1104 +      &&
  1.1105 +      isToplevelProgress
  1.1106 +      &&
  1.1107 +      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
  1.1108 +  {
  1.1109 +    int32_t temp_DocumentRequestsInProgress;
  1.1110 +    nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
  1.1111 +
  1.1112 +    {
  1.1113 +      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1114 +      temp_DocumentRequestsInProgress = mDocumentRequestsInProgress;
  1.1115 +      if (allowSecurityStateChange)
  1.1116 +      {
  1.1117 +        temp_ToplevelEventSink = mToplevelEventSink;
  1.1118 +      }
  1.1119 +    }
  1.1120 +
  1.1121 +    if (temp_DocumentRequestsInProgress <= 0)
  1.1122 +    {
  1.1123 +      // Ignore stop requests unless a document load is in progress
  1.1124 +      // Unfortunately on application start, see some stops without having seen any starts...
  1.1125 +      return NS_OK;
  1.1126 +    }
  1.1127 +
  1.1128 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1129 +           ("SecureUI:%p: OnStateChange: --mDocumentRequestsInProgress\n", this
  1.1130 +            ));
  1.1131 +
  1.1132 +    if (!temp_ToplevelEventSink && channel)
  1.1133 +    {
  1.1134 +      if (allowSecurityStateChange)
  1.1135 +      {
  1.1136 +        ObtainEventSink(channel, temp_ToplevelEventSink);
  1.1137 +      }
  1.1138 +    }
  1.1139 +
  1.1140 +    bool sinkChanged = false;
  1.1141 +    bool inProgress;
  1.1142 +    {
  1.1143 +      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1144 +      if (allowSecurityStateChange)
  1.1145 +      {
  1.1146 +        sinkChanged = (mToplevelEventSink != temp_ToplevelEventSink);
  1.1147 +        mToplevelEventSink = temp_ToplevelEventSink;
  1.1148 +      }
  1.1149 +      --mDocumentRequestsInProgress;
  1.1150 +      inProgress = mDocumentRequestsInProgress > 0;
  1.1151 +    }
  1.1152 +
  1.1153 +    if (allowSecurityStateChange && requestHasTransferedData) {
  1.1154 +      // Data has been transferred for the single toplevel
  1.1155 +      // request. Evaluate the security state.
  1.1156 +
  1.1157 +      // Do this only when the sink has changed.  We update and notify
  1.1158 +      // the state from OnLacationChange, this is actually redundant.
  1.1159 +      // But when the target sink changes between OnLocationChange and
  1.1160 +      // OnStateChange, we have to fire the notification here (again).
  1.1161 +
  1.1162 +      if (sinkChanged || mOnLocationChangeSeen)
  1.1163 +        return EvaluateAndUpdateSecurityState(aRequest, securityInfo, false);
  1.1164 +    }
  1.1165 +    mOnLocationChangeSeen = false;
  1.1166 +
  1.1167 +    if (mRestoreSubrequests && !inProgress)
  1.1168 +    {
  1.1169 +      // We get here when there were no OnLocationChange between 
  1.1170 +      // OnStateChange(START) and OnStateChange(STOP).  Then the load has not
  1.1171 +      // been rendered but has been retargeted in some other way then by external
  1.1172 +      // app handler.  Restore mSubRequests* members to what the current security 
  1.1173 +      // state info holds (it was reset to all zero in OnStateChange(START) 
  1.1174 +      // before).
  1.1175 +      nsCOMPtr<nsIAssociatedContentSecurity> currentContentSecurity;
  1.1176 +      {
  1.1177 +        ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1178 +        currentContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
  1.1179 +
  1.1180 +        // Drop this indication flag, the restore opration is just being
  1.1181 +        // done.
  1.1182 +        mRestoreSubrequests = false;
  1.1183 +
  1.1184 +        // We can do this since the state didn't actually change.
  1.1185 +        mNewToplevelSecurityStateKnown = true;
  1.1186 +      }
  1.1187 +
  1.1188 +      int32_t subBroken = 0;
  1.1189 +      int32_t subNo = 0;
  1.1190 +
  1.1191 +      if (currentContentSecurity)
  1.1192 +      {
  1.1193 +        currentContentSecurity->GetCountSubRequestsBrokenSecurity(&subBroken);
  1.1194 +        currentContentSecurity->GetCountSubRequestsNoSecurity(&subNo);
  1.1195 +        PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI:%p: Restoring subs in STOP from %p to %d,%d\n", 
  1.1196 +          this, currentContentSecurity.get(), subBroken, subNo));      
  1.1197 +      }
  1.1198 +
  1.1199 +      {
  1.1200 +        ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1201 +        mSubRequestsBrokenSecurity = subBroken;
  1.1202 +        mSubRequestsNoSecurity = subNo;
  1.1203 +      }
  1.1204 +    }
  1.1205 +    
  1.1206 +    return NS_OK;
  1.1207 +  }
  1.1208 +  
  1.1209 +  if (aProgressStateFlags & STATE_STOP
  1.1210 +      &&
  1.1211 +      aProgressStateFlags & STATE_IS_REQUEST)
  1.1212 +  {
  1.1213 +    if (!isSubDocumentRelevant)
  1.1214 +      return NS_OK;
  1.1215 +    
  1.1216 +    // if we arrive here, LOAD_DOCUMENT_URI is not set
  1.1217 +    
  1.1218 +    // We only care for the security state of sub requests which have actually transfered data.
  1.1219 +
  1.1220 +    if (allowSecurityStateChange && requestHasTransferedData)
  1.1221 +    {  
  1.1222 +      UpdateSubrequestMembers(securityInfo);
  1.1223 +      
  1.1224 +      // Care for the following scenario:
  1.1225 +      // A new top level document load might have already started,
  1.1226 +      // but the security state of the new top level document might not yet been known.
  1.1227 +      // 
  1.1228 +      // At this point, we are learning about the security state of a sub-document.
  1.1229 +      // We must not update the security state based on the sub content,
  1.1230 +      // if the new top level state is not yet known.
  1.1231 +      //
  1.1232 +      // We skip updating the security state in this case.
  1.1233 +
  1.1234 +      bool temp_NewToplevelSecurityStateKnown;
  1.1235 +      {
  1.1236 +        ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1237 +        temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown;
  1.1238 +      }
  1.1239 +
  1.1240 +      if (temp_NewToplevelSecurityStateKnown)
  1.1241 +        return UpdateSecurityState(aRequest, false, false);
  1.1242 +    }
  1.1243 +
  1.1244 +    return NS_OK;
  1.1245 +  }
  1.1246 +
  1.1247 +  return NS_OK;
  1.1248 +}
  1.1249 +
  1.1250 +// I'm keeping this as a separate function, in order to simplify the review
  1.1251 +// for bug 412456. We should inline this in a follow up patch.
  1.1252 +void nsSecureBrowserUIImpl::ObtainEventSink(nsIChannel *channel, 
  1.1253 +                                            nsCOMPtr<nsISecurityEventSink> &sink)
  1.1254 +{
  1.1255 +  if (!sink)
  1.1256 +    NS_QueryNotificationCallbacks(channel, sink);
  1.1257 +}
  1.1258 +
  1.1259 +nsresult nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest, 
  1.1260 +                                                    bool withNewLocation, 
  1.1261 +                                                    bool withUpdateStatus)
  1.1262 +{
  1.1263 +  lockIconState warnSecurityState = lis_no_security;
  1.1264 +  nsresult rv = NS_OK;
  1.1265 +
  1.1266 +  // both parameters are both input and outout
  1.1267 +  bool flagsChanged = UpdateMyFlags(warnSecurityState);
  1.1268 +
  1.1269 +  if (flagsChanged || withNewLocation || withUpdateStatus)
  1.1270 +    rv = TellTheWorld(warnSecurityState, aRequest);
  1.1271 +
  1.1272 +  return rv;
  1.1273 +}
  1.1274 +
  1.1275 +// must not fail, by definition, only trivial assignments
  1.1276 +// or string operations are allowed
  1.1277 +// returns true if our overall state has changed and we must send out notifications
  1.1278 +bool nsSecureBrowserUIImpl::UpdateMyFlags(lockIconState &warnSecurityState)
  1.1279 +{
  1.1280 +  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1281 +  bool mustTellTheWorld = false;
  1.1282 +
  1.1283 +  lockIconState newSecurityState;
  1.1284 +
  1.1285 +  if (mNewToplevelSecurityState & STATE_IS_SECURE)
  1.1286 +  {
  1.1287 +    if (mSubRequestsBrokenSecurity
  1.1288 +        ||
  1.1289 +        mSubRequestsNoSecurity)
  1.1290 +    {
  1.1291 +      newSecurityState = lis_mixed_security;
  1.1292 +    }
  1.1293 +    else
  1.1294 +    {
  1.1295 +      newSecurityState = lis_high_security;
  1.1296 +    }
  1.1297 +  }
  1.1298 +  else
  1.1299 +  if (mNewToplevelSecurityState & STATE_IS_BROKEN)
  1.1300 +  {
  1.1301 +    // indicating BROKEN is more important than MIXED.
  1.1302 +  
  1.1303 +    newSecurityState = lis_broken_security;
  1.1304 +  }
  1.1305 +  else
  1.1306 +  {
  1.1307 +    newSecurityState = lis_no_security;
  1.1308 +  }
  1.1309 +
  1.1310 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1311 +         ("SecureUI:%p: UpdateSecurityState:  old-new  %d - %d\n", this,
  1.1312 +         mNotifiedSecurityState, newSecurityState
  1.1313 +          ));
  1.1314 +
  1.1315 +  if (mNotifiedSecurityState != newSecurityState)
  1.1316 +  {
  1.1317 +    mustTellTheWorld = true;
  1.1318 +
  1.1319 +    // we'll treat "broken" exactly like "insecure",
  1.1320 +
  1.1321 +    /*
  1.1322 +      security    icon
  1.1323 +      ----------------
  1.1324 +    
  1.1325 +      no          open
  1.1326 +      mixed       broken
  1.1327 +      broken      broken
  1.1328 +      high        high
  1.1329 +    */
  1.1330 +
  1.1331 +    mNotifiedSecurityState = newSecurityState;
  1.1332 +
  1.1333 +    if (lis_no_security == newSecurityState)
  1.1334 +    {
  1.1335 +      mSSLStatus = nullptr;
  1.1336 +    }
  1.1337 +  }
  1.1338 +
  1.1339 +  if (mNotifiedToplevelIsEV != mNewToplevelIsEV) {
  1.1340 +    mustTellTheWorld = true;
  1.1341 +    mNotifiedToplevelIsEV = mNewToplevelIsEV;
  1.1342 +  }
  1.1343 +
  1.1344 +  return mustTellTheWorld;
  1.1345 +}
  1.1346 +
  1.1347 +nsresult nsSecureBrowserUIImpl::TellTheWorld(lockIconState warnSecurityState, 
  1.1348 +                                             nsIRequest* aRequest)
  1.1349 +{
  1.1350 +  nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
  1.1351 +  lockIconState temp_NotifiedSecurityState;
  1.1352 +  bool temp_NotifiedToplevelIsEV;
  1.1353 +
  1.1354 +  {
  1.1355 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1356 +    temp_ToplevelEventSink = mToplevelEventSink;
  1.1357 +    temp_NotifiedSecurityState = mNotifiedSecurityState;
  1.1358 +    temp_NotifiedToplevelIsEV = mNotifiedToplevelIsEV;
  1.1359 +  }
  1.1360 +
  1.1361 +  if (temp_ToplevelEventSink)
  1.1362 +  {
  1.1363 +    uint32_t newState = STATE_IS_INSECURE;
  1.1364 +    MapInternalToExternalState(&newState, 
  1.1365 +                               temp_NotifiedSecurityState, 
  1.1366 +                               temp_NotifiedToplevelIsEV);
  1.1367 +
  1.1368 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1369 +           ("SecureUI:%p: UpdateSecurityState: calling OnSecurityChange\n", this
  1.1370 +            ));
  1.1371 +
  1.1372 +    temp_ToplevelEventSink->OnSecurityChange(aRequest, newState);
  1.1373 +  }
  1.1374 +  else
  1.1375 +  {
  1.1376 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1377 +           ("SecureUI:%p: UpdateSecurityState: NO mToplevelEventSink!\n", this
  1.1378 +            ));
  1.1379 +
  1.1380 +  }
  1.1381 +
  1.1382 +  return NS_OK; 
  1.1383 +}
  1.1384 +
  1.1385 +NS_IMETHODIMP
  1.1386 +nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
  1.1387 +                                        nsIRequest* aRequest,
  1.1388 +                                        nsIURI* aLocation,
  1.1389 +                                        uint32_t aFlags)
  1.1390 +{
  1.1391 +#ifdef DEBUG
  1.1392 +  nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
  1.1393 +  NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
  1.1394 +               "unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
  1.1395 +#endif
  1.1396 +  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1397 +         ("SecureUI:%p: OnLocationChange\n", this));
  1.1398 +
  1.1399 +  bool updateIsViewSource = false;
  1.1400 +  bool temp_IsViewSource = false;
  1.1401 +  nsCOMPtr<nsIDOMWindow> window;
  1.1402 +
  1.1403 +  if (aLocation)
  1.1404 +  {
  1.1405 +    bool vs;
  1.1406 +
  1.1407 +    nsresult rv = aLocation->SchemeIs("view-source", &vs);
  1.1408 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1409 +
  1.1410 +    if (vs) {
  1.1411 +      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1412 +             ("SecureUI:%p: OnLocationChange: view-source\n", this));
  1.1413 +    }
  1.1414 +
  1.1415 +    updateIsViewSource = true;
  1.1416 +    temp_IsViewSource = vs;
  1.1417 +  }
  1.1418 +
  1.1419 +  {
  1.1420 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1421 +    if (updateIsViewSource) {
  1.1422 +      mIsViewSource = temp_IsViewSource;
  1.1423 +    }
  1.1424 +    mCurrentURI = aLocation;
  1.1425 +    window = do_QueryReferent(mWindow);
  1.1426 +    NS_ASSERTION(window, "Window has gone away?!");
  1.1427 +  }
  1.1428 +
  1.1429 +  // When |aRequest| is null, basically we don't trust that document. But if
  1.1430 +  // docshell insists that the document has not changed at all, we will reuse
  1.1431 +  // the previous security state, no matter what |aRequest| may be.
  1.1432 +  if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT)
  1.1433 +    return NS_OK;
  1.1434 +
  1.1435 +  // The location bar has changed, so we must update the security state.  The
  1.1436 +  // only concern with doing this here is that a page may transition from being
  1.1437 +  // reported as completely secure to being reported as partially secure
  1.1438 +  // (mixed).  This may be confusing for users, and it may bother users who
  1.1439 +  // like seeing security dialogs.  However, it seems prudent given that page
  1.1440 +  // loading may never end in some edge cases (perhaps by a site with malicious
  1.1441 +  // intent).
  1.1442 +
  1.1443 +  nsCOMPtr<nsIDOMWindow> windowForProgress;
  1.1444 +  aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
  1.1445 +
  1.1446 +  nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
  1.1447 +
  1.1448 +  if (windowForProgress.get() == window.get()) {
  1.1449 +    // For toplevel channels, update the security state right away.
  1.1450 +    mOnLocationChangeSeen = true;
  1.1451 +    return EvaluateAndUpdateSecurityState(aRequest, securityInfo, true);
  1.1452 +  }
  1.1453 +
  1.1454 +  // For channels in subdocuments we only update our subrequest state members.
  1.1455 +  UpdateSubrequestMembers(securityInfo);
  1.1456 +
  1.1457 +  // Care for the following scenario:
  1.1458 +
  1.1459 +  // A new toplevel document load might have already started, but the security
  1.1460 +  // state of the new toplevel document might not yet be known.
  1.1461 +  // 
  1.1462 +  // At this point, we are learning about the security state of a sub-document.
  1.1463 +  // We must not update the security state based on the sub content, if the new
  1.1464 +  // top level state is not yet known.
  1.1465 +  //
  1.1466 +  // We skip updating the security state in this case.
  1.1467 +
  1.1468 +  bool temp_NewToplevelSecurityStateKnown;
  1.1469 +  {
  1.1470 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1471 +    temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown;
  1.1472 +  }
  1.1473 +
  1.1474 +  if (temp_NewToplevelSecurityStateKnown)
  1.1475 +    return UpdateSecurityState(aRequest, true, false);
  1.1476 +
  1.1477 +  return NS_OK;
  1.1478 +}
  1.1479 +
  1.1480 +NS_IMETHODIMP
  1.1481 +nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress* aWebProgress,
  1.1482 +                                      nsIRequest* aRequest,
  1.1483 +                                      nsresult aStatus,
  1.1484 +                                      const char16_t* aMessage)
  1.1485 +{
  1.1486 +  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
  1.1487 +  return NS_OK;
  1.1488 +}
  1.1489 +
  1.1490 +nsresult
  1.1491 +nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress *aWebProgress,
  1.1492 +                                        nsIRequest *aRequest,
  1.1493 +                                        uint32_t state)
  1.1494 +{
  1.1495 +#if defined(DEBUG)
  1.1496 +  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  1.1497 +  if (!channel)
  1.1498 +    return NS_OK;
  1.1499 +
  1.1500 +  nsCOMPtr<nsIURI> aURI;
  1.1501 +  channel->GetURI(getter_AddRefs(aURI));
  1.1502 +  
  1.1503 +  if (aURI) {
  1.1504 +    nsAutoCString temp;
  1.1505 +    aURI->GetSpec(temp);
  1.1506 +    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1.1507 +           ("SecureUI:%p: OnSecurityChange: (%x) %s\n", this,
  1.1508 +            state, temp.get()));
  1.1509 +  }
  1.1510 +#endif
  1.1511 +
  1.1512 +  return NS_OK;
  1.1513 +}
  1.1514 +
  1.1515 +// nsISSLStatusProvider methods
  1.1516 +NS_IMETHODIMP
  1.1517 +nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result)
  1.1518 +{
  1.1519 +  NS_ENSURE_ARG_POINTER(_result);
  1.1520 +
  1.1521 +  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1522 +
  1.1523 +  switch (mNotifiedSecurityState)
  1.1524 +  {
  1.1525 +    case lis_mixed_security:
  1.1526 +    case lis_high_security:
  1.1527 +      break;
  1.1528 +
  1.1529 +    default:
  1.1530 +      NS_NOTREACHED("if this is reached you must add more entries to the switch");
  1.1531 +    case lis_no_security:
  1.1532 +    case lis_broken_security:
  1.1533 +      *_result = nullptr;
  1.1534 +      return NS_OK;
  1.1535 +  }
  1.1536 + 
  1.1537 +  *_result = mSSLStatus;
  1.1538 +  NS_IF_ADDREF(*_result);
  1.1539 +
  1.1540 +  return NS_OK;
  1.1541 +}
  1.1542 +
  1.1543 +nsresult
  1.1544 +nsSecureBrowserUIImpl::IsURLHTTPS(nsIURI* aURL, bool* value)
  1.1545 +{
  1.1546 +  *value = false;
  1.1547 +
  1.1548 +  if (!aURL)
  1.1549 +    return NS_OK;
  1.1550 +
  1.1551 +  return aURL->SchemeIs("https", value);
  1.1552 +}
  1.1553 +
  1.1554 +nsresult
  1.1555 +nsSecureBrowserUIImpl::IsURLJavaScript(nsIURI* aURL, bool* value)
  1.1556 +{
  1.1557 +  *value = false;
  1.1558 +
  1.1559 +  if (!aURL)
  1.1560 +    return NS_OK;
  1.1561 +
  1.1562 +  return aURL->SchemeIs("javascript", value);
  1.1563 +}
  1.1564 +
  1.1565 +nsresult
  1.1566 +nsSecureBrowserUIImpl::CheckPost(nsIURI *formURL, nsIURI *actionURL, bool *okayToPost)
  1.1567 +{
  1.1568 +  bool formSecure, actionSecure, actionJavaScript;
  1.1569 +  *okayToPost = true;
  1.1570 +
  1.1571 +  nsresult rv = IsURLHTTPS(formURL, &formSecure);
  1.1572 +  if (NS_FAILED(rv))
  1.1573 +    return rv;
  1.1574 +
  1.1575 +  rv = IsURLHTTPS(actionURL, &actionSecure);
  1.1576 +  if (NS_FAILED(rv))
  1.1577 +    return rv;
  1.1578 +
  1.1579 +  rv = IsURLJavaScript(actionURL, &actionJavaScript);
  1.1580 +  if (NS_FAILED(rv))
  1.1581 +    return rv;
  1.1582 +
  1.1583 +  // If we are posting to a secure link, all is okay.
  1.1584 +  // It doesn't matter whether the currently viewed page is secure or not,
  1.1585 +  // because the data will be sent to a secure URL.
  1.1586 +  if (actionSecure) {
  1.1587 +    return NS_OK;
  1.1588 +  }
  1.1589 +
  1.1590 +  // Action is a JavaScript call, not an actual post. That's okay too.
  1.1591 +  if (actionJavaScript) {
  1.1592 +    return NS_OK;
  1.1593 +  }
  1.1594 +
  1.1595 +  // posting to insecure webpage from a secure webpage.
  1.1596 +  if (formSecure) {
  1.1597 +    *okayToPost = ConfirmPostToInsecureFromSecure();
  1.1598 +  }
  1.1599 +
  1.1600 +  return NS_OK;
  1.1601 +}
  1.1602 +
  1.1603 +//
  1.1604 +// Implementation of an nsIInterfaceRequestor for use
  1.1605 +// as context for NSS calls
  1.1606 +//
  1.1607 +class nsUIContext : public nsIInterfaceRequestor
  1.1608 +{
  1.1609 +public:
  1.1610 +  NS_DECL_ISUPPORTS
  1.1611 +  NS_DECL_NSIINTERFACEREQUESTOR
  1.1612 +
  1.1613 +  nsUIContext(nsIDOMWindow *window);
  1.1614 +  virtual ~nsUIContext();
  1.1615 +
  1.1616 +private:
  1.1617 +  nsCOMPtr<nsIDOMWindow> mWindow;
  1.1618 +};
  1.1619 +
  1.1620 +NS_IMPL_ISUPPORTS(nsUIContext, nsIInterfaceRequestor)
  1.1621 +
  1.1622 +nsUIContext::nsUIContext(nsIDOMWindow *aWindow)
  1.1623 +: mWindow(aWindow)
  1.1624 +{
  1.1625 +}
  1.1626 +
  1.1627 +nsUIContext::~nsUIContext()
  1.1628 +{
  1.1629 +}
  1.1630 +
  1.1631 +/* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
  1.1632 +NS_IMETHODIMP nsUIContext::GetInterface(const nsIID & uuid, void * *result)
  1.1633 +{
  1.1634 +  NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
  1.1635 +  nsresult rv;
  1.1636 +
  1.1637 +  if (uuid.Equals(NS_GET_IID(nsIPrompt))) {
  1.1638 +    nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mWindow, &rv);
  1.1639 +    if (NS_FAILED(rv)) return rv;
  1.1640 +
  1.1641 +    nsIPrompt *prompt;
  1.1642 +
  1.1643 +    rv = window->GetPrompter(&prompt);
  1.1644 +    *result = prompt;
  1.1645 +  } else if (uuid.Equals(NS_GET_IID(nsIDOMWindow))) {
  1.1646 +    *result = mWindow;
  1.1647 +    NS_ADDREF ((nsISupports*) *result);
  1.1648 +    rv = NS_OK;
  1.1649 +  } else {
  1.1650 +    rv = NS_ERROR_NO_INTERFACE;
  1.1651 +  }
  1.1652 +
  1.1653 +  return rv;
  1.1654 +}
  1.1655 +
  1.1656 +bool
  1.1657 +nsSecureBrowserUIImpl::GetNSSDialogs(nsCOMPtr<nsISecurityWarningDialogs> & dialogs,
  1.1658 +                                     nsCOMPtr<nsIInterfaceRequestor> & ctx)
  1.1659 +{
  1.1660 +  if (!NS_IsMainThread()) {
  1.1661 +    NS_ERROR("nsSecureBrowserUIImpl::GetNSSDialogs called off the main thread");
  1.1662 +    return false;
  1.1663 +  }
  1.1664 +
  1.1665 +  dialogs = do_GetService(NS_SECURITYWARNINGDIALOGS_CONTRACTID);
  1.1666 +  if (!dialogs)
  1.1667 +    return false;
  1.1668 +
  1.1669 +  nsCOMPtr<nsIDOMWindow> window;
  1.1670 +  {
  1.1671 +    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1.1672 +    window = do_QueryReferent(mWindow);
  1.1673 +    NS_ASSERTION(window, "Window has gone away?!");
  1.1674 +  }
  1.1675 +  ctx = new nsUIContext(window);
  1.1676 +  
  1.1677 +  return true;
  1.1678 +}
  1.1679 +
  1.1680 +/**
  1.1681 + * ConfirmPostToInsecureFromSecure - returns true if
  1.1682 + *   the user approves the submit (or doesn't care).
  1.1683 + *   returns false on errors.
  1.1684 + */
  1.1685 +bool nsSecureBrowserUIImpl::
  1.1686 +ConfirmPostToInsecureFromSecure()
  1.1687 +{
  1.1688 +  nsCOMPtr<nsISecurityWarningDialogs> dialogs;
  1.1689 +  nsCOMPtr<nsIInterfaceRequestor> ctx;
  1.1690 +
  1.1691 +  if (!GetNSSDialogs(dialogs, ctx)) {
  1.1692 +    return false; // Should this allow true for unimplemented?
  1.1693 +  }
  1.1694 +
  1.1695 +  bool result;
  1.1696 +
  1.1697 +  nsresult rv = dialogs->ConfirmPostToInsecureFromSecure(ctx, &result);
  1.1698 +  if (NS_FAILED(rv)) return false;
  1.1699 +
  1.1700 +  return result;
  1.1701 +}

mercurial