PageRenderTime 125ms CodeModel.GetById 18ms app.highlight 96ms RepoModel.GetById 1ms app.codeStats 0ms

/security/manager/boot/src/nsSecureBrowserUIImpl.cpp

http://github.com/zpao/v8monkey
C++ | 1942 lines | 1363 code | 290 blank | 289 comment | 202 complexity | 8cfa5f8b1a6419a32ef2874d656d2a26 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
   2/* ***** BEGIN LICENSE BLOCK *****
   3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   4 *
   5 * The contents of this file are subject to the Mozilla Public License Version
   6 * 1.1 (the "License"); you may not use this file except in compliance with
   7 * the License. You may obtain a copy of the License at
   8 * http://www.mozilla.org/MPL/
   9 *
  10 * Software distributed under the License is distributed on an "AS IS" basis,
  11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12 * for the specific language governing rights and limitations under the
  13 * License.
  14 *
  15 * The Original Code is mozilla.org code.
  16 *
  17 * The Initial Developer of the Original Code is
  18 * Netscape Communications Corporation.
  19 * Portions created by the Initial Developer are Copyright (C) 1998-2001
  20 * the Initial Developer. All Rights Reserved.
  21 *
  22 * Contributor(s):
  23 *   Hubbie Shaw
  24 *   Doug Turner <dougt@netscape.com>
  25 *   Stuart Parmenter <pavlov@netscape.com>
  26 *   Brian Ryner <bryner@brianryner.com>
  27 *   Terry Hayes <thayes@netscape.com>
  28 *   Kai Engert <kaie@netscape.com>
  29 *
  30 * Alternatively, the contents of this file may be used under the terms of
  31 * either the GNU General Public License Version 2 or later (the "GPL"), or
  32 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  33 * in which case the provisions of the GPL or the LGPL are applicable instead
  34 * of those above. If you wish to allow use of your version of this file only
  35 * under the terms of either the GPL or the LGPL, and not to allow others to
  36 * use your version of this file under the terms of the MPL, indicate your
  37 * decision by deleting the provisions above and replace them with the notice
  38 * and other provisions required by the GPL or the LGPL. If you do not delete
  39 * the provisions above, a recipient may use your version of this file under
  40 * the terms of any one of the MPL, the GPL or the LGPL.
  41 *
  42 * ***** END LICENSE BLOCK ***** */
  43
  44#ifdef MOZ_LOGGING
  45#define FORCE_PR_LOG
  46#endif
  47
  48#include "nspr.h"
  49#include "prlog.h"
  50#include "prmem.h"
  51
  52#include "nsISecureBrowserUI.h"
  53#include "nsSecureBrowserUIImpl.h"
  54#include "nsCOMPtr.h"
  55#include "nsIInterfaceRequestor.h"
  56#include "nsIInterfaceRequestorUtils.h"
  57#include "nsIServiceManager.h"
  58#include "nsIObserverService.h"
  59#include "nsCURILoader.h"
  60#include "nsIDocShell.h"
  61#include "nsIDocument.h"
  62#include "nsIPrincipal.h"
  63#include "nsIDOMElement.h"
  64#include "nsPIDOMWindow.h"
  65#include "nsIContent.h"
  66#include "nsIWebProgress.h"
  67#include "nsIWebProgressListener.h"
  68#include "nsIChannel.h"
  69#include "nsIHttpChannel.h"
  70#include "nsIFileChannel.h"
  71#include "nsIWyciwygChannel.h"
  72#include "nsIFTPChannel.h"
  73#include "nsITransportSecurityInfo.h"
  74#include "nsISSLStatus.h"
  75#include "nsIURI.h"
  76#include "nsISecurityEventSink.h"
  77#include "nsIPrompt.h"
  78#include "nsIFormSubmitObserver.h"
  79#include "nsISecurityWarningDialogs.h"
  80#include "nsISecurityInfoProvider.h"
  81#include "imgIRequest.h"
  82#include "nsThreadUtils.h"
  83#include "nsNetUtil.h"
  84#include "nsNetCID.h"
  85#include "nsCRT.h"
  86
  87using namespace mozilla;
  88
  89#define SECURITY_STRING_BUNDLE_URL "chrome://pipnss/locale/security.properties"
  90
  91#define IS_SECURE(state) ((state & 0xFFFF) == STATE_IS_SECURE)
  92
  93#if defined(PR_LOGGING)
  94//
  95// Log module for nsSecureBrowserUI logging...
  96//
  97// To enable logging (see prlog.h for full details):
  98//
  99//    set NSPR_LOG_MODULES=nsSecureBrowserUI:5
 100//    set NSPR_LOG_FILE=nspr.log
 101//
 102// this enables PR_LOG_DEBUG level information and places all output in
 103// the file nspr.log
 104//
 105PRLogModuleInfo* gSecureDocLog = nsnull;
 106#endif /* PR_LOGGING */
 107
 108struct RequestHashEntry : PLDHashEntryHdr {
 109    void *r;
 110};
 111
 112PR_STATIC_CALLBACK(bool)
 113RequestMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
 114                         const void *key)
 115{
 116  const RequestHashEntry *entry = static_cast<const RequestHashEntry*>(hdr);
 117  return entry->r == key;
 118}
 119
 120PR_STATIC_CALLBACK(bool)
 121RequestMapInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
 122                     const void *key)
 123{
 124  RequestHashEntry *entry = static_cast<RequestHashEntry*>(hdr);
 125  entry->r = (void*)key;
 126  return true;
 127}
 128
 129static PLDHashTableOps gMapOps = {
 130  PL_DHashAllocTable,
 131  PL_DHashFreeTable,
 132  PL_DHashVoidPtrKeyStub,
 133  RequestMapMatchEntry,
 134  PL_DHashMoveEntryStub,
 135  PL_DHashClearEntryStub,
 136  PL_DHashFinalizeStub,
 137  RequestMapInitEntry
 138};
 139
 140#ifdef DEBUG
 141class nsAutoAtomic {
 142  public:
 143    nsAutoAtomic(PRInt32 &i)
 144    :mI(i) {
 145      PR_ATOMIC_INCREMENT(&mI);
 146    }
 147
 148    ~nsAutoAtomic() {
 149      PR_ATOMIC_DECREMENT(&mI);
 150    }
 151
 152  protected:
 153    PRInt32 &mI;
 154
 155  private:
 156    nsAutoAtomic(); // not accessible
 157};
 158#endif
 159
 160nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
 161  : mReentrantMonitor("nsSecureBrowserUIImpl.mReentrantMonitor")
 162  , mNotifiedSecurityState(lis_no_security)
 163  , mNotifiedToplevelIsEV(false)
 164  , mNewToplevelSecurityState(STATE_IS_INSECURE)
 165  , mNewToplevelIsEV(false)
 166  , mNewToplevelSecurityStateKnown(true)
 167  , mIsViewSource(false)
 168  , mSubRequestsHighSecurity(0)
 169  , mSubRequestsLowSecurity(0)
 170  , mSubRequestsBrokenSecurity(0)
 171  , mSubRequestsNoSecurity(0)
 172#ifdef DEBUG
 173  , mOnStateLocationChangeReentranceDetection(0)
 174#endif
 175{
 176  mTransferringRequests.ops = nsnull;
 177  ResetStateTracking();
 178  
 179#if defined(PR_LOGGING)
 180  if (!gSecureDocLog)
 181    gSecureDocLog = PR_NewLogModule("nsSecureBrowserUI");
 182#endif /* PR_LOGGING */
 183}
 184
 185nsSecureBrowserUIImpl::~nsSecureBrowserUIImpl()
 186{
 187  if (mTransferringRequests.ops) {
 188    PL_DHashTableFinish(&mTransferringRequests);
 189    mTransferringRequests.ops = nsnull;
 190  }
 191}
 192
 193NS_IMPL_THREADSAFE_ISUPPORTS6(nsSecureBrowserUIImpl,
 194                              nsISecureBrowserUI,
 195                              nsIWebProgressListener,
 196                              nsIFormSubmitObserver,
 197                              nsIObserver,
 198                              nsISupportsWeakReference,
 199                              nsISSLStatusProvider)
 200
 201NS_IMETHODIMP
 202nsSecureBrowserUIImpl::Init(nsIDOMWindow *aWindow)
 203{
 204
 205#ifdef PR_LOGGING
 206  nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
 207
 208  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 209         ("SecureUI:%p: Init: mWindow: %p, aWindow: %p\n", this,
 210          window.get(), aWindow));
 211#endif
 212
 213  if (!aWindow) {
 214    NS_WARNING("Null window passed to nsSecureBrowserUIImpl::Init()");
 215    return NS_ERROR_INVALID_ARG;
 216  }
 217
 218  if (mWindow) {
 219    NS_WARNING("Trying to init an nsSecureBrowserUIImpl twice");
 220    return NS_ERROR_ALREADY_INITIALIZED;
 221  }
 222
 223  nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(aWindow));
 224  if (pwin->IsInnerWindow()) {
 225    pwin = pwin->GetOuterWindow();
 226  }
 227
 228  nsresult rv;
 229  mWindow = do_GetWeakReference(pwin, &rv);
 230  NS_ENSURE_SUCCESS(rv, rv);
 231
 232  nsCOMPtr<nsIStringBundleService> service(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
 233  if (NS_FAILED(rv)) return rv;
 234  
 235  // We do not need to test for mStringBundle here...
 236  // Anywhere we use it, we will test before using.  Some
 237  // embedded users of PSM may want to reuse our
 238  // nsSecureBrowserUIImpl implementation without the
 239  // bundle.
 240  service->CreateBundle(SECURITY_STRING_BUNDLE_URL, getter_AddRefs(mStringBundle));
 241  
 242  
 243  // hook up to the form post notifications:
 244  nsCOMPtr<nsIObserverService> svc(do_GetService("@mozilla.org/observer-service;1", &rv));
 245  if (NS_SUCCEEDED(rv)) {
 246    rv = svc->AddObserver(this, NS_FORMSUBMIT_SUBJECT, true);
 247  }
 248  
 249  nsCOMPtr<nsPIDOMWindow> piwindow(do_QueryInterface(aWindow));
 250  if (!piwindow) return NS_ERROR_FAILURE;
 251
 252  nsIDocShell *docShell = piwindow->GetDocShell();
 253
 254  // The Docshell will own the SecureBrowserUI object
 255  if (!docShell)
 256    return NS_ERROR_FAILURE;
 257
 258  docShell->SetSecurityUI(this);
 259
 260  /* GetWebProgress(mWindow) */
 261  // hook up to the webprogress notifications.
 262  nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
 263  if (!wp) return NS_ERROR_FAILURE;
 264  /* end GetWebProgress */
 265  
 266  wp->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
 267                          nsIWebProgress::NOTIFY_STATE_ALL | 
 268                          nsIWebProgress::NOTIFY_LOCATION  |
 269                          nsIWebProgress::NOTIFY_SECURITY);
 270
 271
 272  return NS_OK;
 273}
 274
 275NS_IMETHODIMP
 276nsSecureBrowserUIImpl::GetState(PRUint32* aState)
 277{
 278  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 279  return MapInternalToExternalState(aState, mNotifiedSecurityState, mNotifiedToplevelIsEV);
 280}
 281
 282// static
 283already_AddRefed<nsISupports> 
 284nsSecureBrowserUIImpl::ExtractSecurityInfo(nsIRequest* aRequest)
 285{
 286  nsISupports *retval = nsnull; 
 287  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
 288  if (channel)
 289    channel->GetSecurityInfo(&retval);
 290  
 291  if (!retval) {
 292    nsCOMPtr<nsISecurityInfoProvider> provider(do_QueryInterface(aRequest));
 293    if (provider)
 294      provider->GetSecurityInfo(&retval);
 295  }
 296
 297  return retval;
 298}
 299
 300nsresult
 301nsSecureBrowserUIImpl::MapInternalToExternalState(PRUint32* aState, lockIconState lock, bool ev)
 302{
 303  NS_ENSURE_ARG(aState);
 304
 305  switch (lock)
 306  {
 307    case lis_broken_security:
 308      *aState = STATE_IS_BROKEN;
 309      break;
 310
 311    case lis_mixed_security:
 312      *aState = STATE_IS_BROKEN;
 313      break;
 314
 315    case lis_low_security:
 316      *aState = STATE_IS_SECURE | STATE_SECURE_LOW;
 317      break;
 318
 319    case lis_high_security:
 320      *aState = STATE_IS_SECURE | STATE_SECURE_HIGH;
 321      break;
 322
 323    default:
 324    case lis_no_security:
 325      *aState = STATE_IS_INSECURE;
 326      break;
 327  }
 328
 329  if (ev && (*aState & STATE_IS_SECURE))
 330    *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
 331  
 332  return NS_OK;
 333}
 334
 335NS_IMETHODIMP
 336nsSecureBrowserUIImpl::GetTooltipText(nsAString& aText)
 337{
 338  lockIconState state;
 339  nsXPIDLString tooltip;
 340
 341  {
 342    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 343    state = mNotifiedSecurityState;
 344    tooltip = mInfoTooltip;
 345  }
 346
 347  if (state == lis_mixed_security)
 348  {
 349    GetBundleString(NS_LITERAL_STRING("SecurityButtonMixedContentTooltipText").get(),
 350                    aText);
 351  }
 352  else if (!tooltip.IsEmpty())
 353  {
 354    aText = tooltip;
 355  }
 356  else
 357  {
 358    GetBundleString(NS_LITERAL_STRING("SecurityButtonTooltipText").get(),
 359                    aText);
 360  }
 361
 362  return NS_OK;
 363}
 364
 365NS_IMETHODIMP
 366nsSecureBrowserUIImpl::Observe(nsISupports*, const char*,
 367                               const PRUnichar*)
 368{
 369  return NS_ERROR_NOT_IMPLEMENTED;
 370}
 371
 372
 373static nsresult IsChildOfDomWindow(nsIDOMWindow *parent, nsIDOMWindow *child,
 374                                   bool* value)
 375{
 376  *value = false;
 377  
 378  if (parent == child) {
 379    *value = true;
 380    return NS_OK;
 381  }
 382  
 383  nsCOMPtr<nsIDOMWindow> childsParent;
 384  child->GetParent(getter_AddRefs(childsParent));
 385  
 386  if (childsParent && childsParent.get() != child)
 387    IsChildOfDomWindow(parent, childsParent, value);
 388  
 389  return NS_OK;
 390}
 391
 392static PRUint32 GetSecurityStateFromSecurityInfo(nsISupports *info)
 393{
 394  nsresult res;
 395  PRUint32 securityState;
 396
 397  nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
 398  if (!psmInfo) {
 399    PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - no nsITransportSecurityInfo for %p\n",
 400                                         (nsISupports *)info));
 401    return nsIWebProgressListener::STATE_IS_INSECURE;
 402  }
 403  PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - info is %p\n", 
 404                                       (nsISupports *)info));
 405  
 406  res = psmInfo->GetSecurityState(&securityState);
 407  if (NS_FAILED(res)) {
 408    PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - GetSecurityState failed: %d\n",
 409                                         res));
 410    securityState = nsIWebProgressListener::STATE_IS_BROKEN;
 411  }
 412  
 413  PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - Returning %d\n", 
 414                                       securityState));
 415  return securityState;
 416}
 417
 418
 419NS_IMETHODIMP
 420nsSecureBrowserUIImpl::Notify(nsIDOMHTMLFormElement* aDOMForm,
 421                              nsIDOMWindow* aWindow, nsIURI* actionURL,
 422                              bool* cancelSubmit)
 423{
 424  // Return NS_OK unless we want to prevent this form from submitting.
 425  *cancelSubmit = false;
 426  if (!aWindow || !actionURL || !aDOMForm)
 427    return NS_OK;
 428  
 429  nsCOMPtr<nsIContent> formNode = do_QueryInterface(aDOMForm);
 430
 431  nsCOMPtr<nsIDocument> document = formNode->GetDocument();
 432  if (!document) return NS_OK;
 433
 434  nsIPrincipal *principal = formNode->NodePrincipal();
 435  
 436  if (!principal)
 437  {
 438    *cancelSubmit = true;
 439    return NS_OK;
 440  }
 441
 442  nsCOMPtr<nsIURI> formURL;
 443  if (NS_FAILED(principal->GetURI(getter_AddRefs(formURL))) ||
 444      !formURL)
 445  {
 446    formURL = document->GetDocumentURI();
 447  }
 448
 449  nsCOMPtr<nsIDOMWindow> postingWindow =
 450    do_QueryInterface(document->GetWindow());
 451  // We can't find this document's window, cancel it.
 452  if (!postingWindow)
 453  {
 454    NS_WARNING("If you see this and can explain why it should be allowed, note in Bug 332324");
 455    *cancelSubmit = true;
 456    return NS_OK;
 457  }
 458
 459  nsCOMPtr<nsIDOMWindow> window;
 460  {
 461    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 462    window = do_QueryReferent(mWindow);
 463    NS_ASSERTION(window, "Window has gone away?!");
 464  }
 465
 466  bool isChild;
 467  IsChildOfDomWindow(window, postingWindow, &isChild);
 468  
 469  // This notify call is not for our window, ignore it.
 470  if (!isChild)
 471    return NS_OK;
 472  
 473  bool okayToPost;
 474  nsresult res = CheckPost(formURL, actionURL, &okayToPost);
 475  
 476  if (NS_SUCCEEDED(res) && !okayToPost)
 477    *cancelSubmit = true;
 478  
 479  return res;
 480}
 481
 482//  nsIWebProgressListener
 483NS_IMETHODIMP 
 484nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress* aWebProgress,
 485                                        nsIRequest* aRequest,
 486                                        PRInt32 aCurSelfProgress,
 487                                        PRInt32 aMaxSelfProgress,
 488                                        PRInt32 aCurTotalProgress,
 489                                        PRInt32 aMaxTotalProgress)
 490{
 491  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
 492  return NS_OK;
 493}
 494
 495void nsSecureBrowserUIImpl::ResetStateTracking()
 496{
 497  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 498
 499  mInfoTooltip.Truncate();
 500  mDocumentRequestsInProgress = 0;
 501  if (mTransferringRequests.ops) {
 502    PL_DHashTableFinish(&mTransferringRequests);
 503    mTransferringRequests.ops = nsnull;
 504  }
 505  PL_DHashTableInit(&mTransferringRequests, &gMapOps, nsnull,
 506                    sizeof(RequestHashEntry), 16);
 507}
 508
 509nsresult
 510nsSecureBrowserUIImpl::EvaluateAndUpdateSecurityState(nsIRequest* aRequest, nsISupports *info,
 511                                                      bool withNewLocation)
 512{
 513  /* I explicitly ignore the camelCase variable naming style here,
 514     I want to make it clear these are temp variables that relate to the 
 515     member variables with the same suffix.*/
 516
 517  PRUint32 temp_NewToplevelSecurityState = nsIWebProgressListener::STATE_IS_INSECURE;
 518  bool temp_NewToplevelIsEV = false;
 519
 520  bool updateStatus = false;
 521  nsCOMPtr<nsISSLStatus> temp_SSLStatus;
 522
 523  bool updateTooltip = false;
 524  nsXPIDLString temp_InfoTooltip;
 525
 526    temp_NewToplevelSecurityState = GetSecurityStateFromSecurityInfo(info);
 527
 528    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 529           ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n", this,
 530            temp_NewToplevelSecurityState));
 531
 532    nsCOMPtr<nsISSLStatusProvider> sp = do_QueryInterface(info);
 533    if (sp) {
 534      // Ignore result
 535      updateStatus = true;
 536      (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
 537      if (temp_SSLStatus) {
 538        bool aTemp;
 539        if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) {
 540          temp_NewToplevelIsEV = aTemp;
 541        }
 542      }
 543    }
 544
 545    if (info) {
 546      nsCOMPtr<nsITransportSecurityInfo> secInfo(do_QueryInterface(info));
 547      if (secInfo) {
 548        updateTooltip = true;
 549        secInfo->GetShortSecurityDescription(getter_Copies(temp_InfoTooltip));
 550      }
 551    }
 552
 553  // assume temp_NewToplevelSecurityState was set in this scope!
 554  // see code that is directly above
 555
 556  {
 557    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 558    mNewToplevelSecurityStateKnown = true;
 559    mNewToplevelSecurityState = temp_NewToplevelSecurityState;
 560    mNewToplevelIsEV = temp_NewToplevelIsEV;
 561    if (updateStatus) {
 562      mSSLStatus = temp_SSLStatus;
 563    }
 564    if (updateTooltip) {
 565      mInfoTooltip = temp_InfoTooltip;
 566    }
 567    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 568           ("SecureUI:%p: remember securityInfo %p\n", this,
 569            info));
 570    nsCOMPtr<nsIAssociatedContentSecurity> associatedContentSecurityFromRequest =
 571        do_QueryInterface(aRequest);
 572    if (associatedContentSecurityFromRequest)
 573        mCurrentToplevelSecurityInfo = aRequest;
 574    else
 575        mCurrentToplevelSecurityInfo = info;
 576  }
 577
 578  return UpdateSecurityState(aRequest, withNewLocation, 
 579                             updateStatus, updateTooltip);
 580}
 581
 582void
 583nsSecureBrowserUIImpl::UpdateSubrequestMembers(nsISupports *securityInfo)
 584{
 585  // For wyciwyg channels in subdocuments we only update our
 586  // subrequest state members.
 587  PRUint32 reqState = GetSecurityStateFromSecurityInfo(securityInfo);
 588
 589  // the code above this line should run without a lock
 590  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 591
 592  if (reqState & STATE_IS_SECURE) {
 593    if (reqState & STATE_SECURE_LOW || reqState & STATE_SECURE_MED) {
 594      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 595             ("SecureUI:%p: OnStateChange: subreq LOW\n", this));
 596      ++mSubRequestsLowSecurity;
 597    } else {
 598      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 599             ("SecureUI:%p: OnStateChange: subreq HIGH\n", this));
 600      ++mSubRequestsHighSecurity;
 601    }
 602  } else if (reqState & STATE_IS_BROKEN) {
 603    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 604           ("SecureUI:%p: OnStateChange: subreq BROKEN\n", this));
 605    ++mSubRequestsBrokenSecurity;
 606  } else {
 607    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 608           ("SecureUI:%p: OnStateChange: subreq INSECURE\n", this));
 609    ++mSubRequestsNoSecurity;
 610  }
 611}
 612
 613NS_IMETHODIMP
 614nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
 615                                     nsIRequest* aRequest,
 616                                     PRUint32 aProgressStateFlags,
 617                                     nsresult aStatus)
 618{
 619#ifdef DEBUG
 620  nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
 621  NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
 622               "unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
 623#endif
 624  /*
 625    All discussion, unless otherwise mentioned, only refers to
 626    http, https, file or wyciwig requests.
 627
 628
 629    Redirects are evil, well, some of them.
 630    There are multiple forms of redirects.
 631
 632    Redirects caused by http refresh content are ok, because experiments show,
 633    with those redirects, the old page contents and their requests will come to STOP
 634    completely, before any progress from new refreshed page content is reported.
 635    So we can safely treat them as separate page loading transactions.
 636
 637    Evil are redirects at the http protocol level, like code 302.
 638
 639    If the toplevel documents gets replaced, i.e. redirected with 302, we do not care for the 
 640    security state of the initial transaction, which has now been redirected, 
 641    we only care for the new page load.
 642    
 643    For the implementation of the security UI, we make an assumption, that is hopefully true.
 644    
 645    Imagine, the received page that was delivered with the 302 redirection answer,
 646    also delivered html content.
 647
 648    What happens if the parser starts to analyze the content and tries to load contained sub objects?
 649    
 650    In that case we would see start and stop requests for subdocuments, some for the previous document,
 651    some for the new target document. And only those for the new toplevel document may be
 652    taken into consideration, when deciding about the security state of the next toplevel document.
 653    
 654    Because security state is being looked at, when loading stops for (sub)documents, this 
 655    could cause real confusion, because we have to decide, whether an incoming progress 
 656    belongs to the new toplevel page, or the previous, already redirected page.
 657    
 658    Can we simplify here?
 659    
 660    If a redirect at the http protocol level is seen, can we safely assume, its html content
 661    will not be parsed, anylzed, and no embedded objects will get loaded (css, js, images),
 662    because the redirect is already happening?
 663    
 664    If we can assume that, this really simplify things. Because we will never see notification
 665    for sub requests that need to get ignored.
 666    
 667    I would like to make this assumption for now, but please let me (kaie) know if I'm wrong.
 668    
 669    Excurse:
 670      If my assumption is wrong, then we would require more tracking information.
 671      We need to keep lists of all pointers to request object that had been seen since the
 672      last toplevel start event.
 673      If the start for a redirected page is seen, the list of releveant object must be cleared,
 674      and only progress for requests which start after it must be analyzed.
 675      All other events must be ignored, as they belong to now irrelevant previous top level documents.
 676
 677
 678    Frames are also evil.
 679
 680    First we need a decision.
 681    kaie thinks: 
 682      Only if the toplevel frame is secure, we should try to display secure lock icons.
 683      If some of the inner contents are insecure, we display mixed mode.
 684      
 685      But if the top level frame is not secure, why indicate a mixed lock icon at all?
 686      I think we should always display an open lock icon, if the top level frameset is insecure.
 687      
 688      That's the way Netscape Communicator behaves, and I think we should do the same.
 689      
 690      The user will not know which parts are secure and which are not,
 691      and any certificate information, displayed in the tooltip or in the "page info"
 692      will only be relevant for some subframe(s), and the user will not know which ones,
 693      so we shouldn't display it as a general attribute of the displayed page.
 694
 695    Why are frames evil?
 696    
 697    Because the progress for the toplevel frame document is not easily distinguishable
 698    from subframes. The same STATE bits are reported.
 699
 700    While at first sight, when a new page load happens,
 701    the toplevel frameset document has also the STATE_IS_NETWORK bit in it.
 702    But this can't really be used. Because in case that document causes a http 302 redirect, 
 703    the real top level frameset will no longer have that bit.
 704    
 705    But we need some way to distinguish top level frames from inner frames.
 706    
 707    I saw that the web progress we get delivered has a reference to the toplevel DOM window.
 708    
 709    I suggest, we look at all incoming requests.
 710    If a request is NOT for the toplevel DOM window, we will always treat it as a subdocument request,
 711    regardless of whether the load flags indicate a top level document.
 712  */
 713
 714  nsCOMPtr<nsIDOMWindow> windowForProgress;
 715  aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
 716
 717  nsCOMPtr<nsIDOMWindow> window;
 718  bool isViewSource;
 719
 720  nsCOMPtr<nsINetUtil> ioService;
 721
 722  {
 723    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 724    window = do_QueryReferent(mWindow);
 725    NS_ASSERTION(window, "Window has gone away?!");
 726    isViewSource = mIsViewSource;
 727    ioService = mIOService;
 728  }
 729
 730  if (!ioService)
 731  {
 732    ioService = do_GetService(NS_IOSERVICE_CONTRACTID);
 733    if (ioService)
 734    {
 735      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
 736      mIOService = ioService;
 737    }
 738  }
 739
 740  bool isNoContentResponse = false;
 741  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
 742  if (httpChannel) 
 743  {
 744    PRUint32 response;
 745    isNoContentResponse = NS_SUCCEEDED(httpChannel->GetResponseStatus(&response)) &&
 746        (response == 204 || response == 205);
 747  }
 748  const bool isToplevelProgress = (windowForProgress.get() == window.get()) && !isNoContentResponse;
 749  
 750#ifdef PR_LOGGING
 751  if (windowForProgress)
 752  {
 753    if (isToplevelProgress)
 754    {
 755      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 756             ("SecureUI:%p: OnStateChange: progress: for toplevel\n", this));
 757    }
 758    else
 759    {
 760      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 761             ("SecureUI:%p: OnStateChange: progress: for something else\n", this));
 762    }
 763  }
 764  else
 765  {
 766      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 767             ("SecureUI:%p: OnStateChange: progress: no window known\n", this));
 768  }
 769#endif
 770
 771  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 772         ("SecureUI:%p: OnStateChange\n", this));
 773
 774  if (isViewSource)
 775    return NS_OK;
 776
 777  if (!aRequest)
 778  {
 779    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 780           ("SecureUI:%p: OnStateChange with null request\n", this));
 781    return NS_ERROR_NULL_POINTER;
 782  }
 783
 784#ifdef PR_LOGGING
 785  if (PR_LOG_TEST(gSecureDocLog, PR_LOG_DEBUG)) {
 786    nsXPIDLCString reqname;
 787    aRequest->GetName(reqname);
 788    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 789           ("SecureUI:%p: %p %p OnStateChange %x %s\n", this, aWebProgress,
 790            aRequest, aProgressStateFlags, reqname.get()));
 791  }
 792#endif
 793
 794  nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
 795
 796  nsCOMPtr<nsIURI> uri;
 797  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
 798  if (channel)
 799  {
 800    channel->GetURI(getter_AddRefs(uri));
 801    if (uri)
 802    {
 803      bool vs;
 804      if (NS_SUCCEEDED(uri->SchemeIs("javascript", &vs)) && vs)
 805      {
 806        // We ignore the progress events for javascript URLs.
 807        // If a document loading gets triggered, we will see more events.
 808        return NS_OK;
 809      }
 810    }
 811  }
 812
 813  PRUint32 loadFlags = 0;
 814  aRequest->GetLoadFlags(&loadFlags);
 815
 816#ifdef PR_LOGGING
 817  if (aProgressStateFlags & STATE_START
 818      &&
 819      aProgressStateFlags & STATE_IS_REQUEST
 820      &&
 821      isToplevelProgress
 822      &&
 823      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
 824  {
 825    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 826           ("SecureUI:%p: OnStateChange: SOMETHING STARTS FOR TOPMOST DOCUMENT\n", this));
 827  }
 828
 829  if (aProgressStateFlags & STATE_STOP
 830      &&
 831      aProgressStateFlags & STATE_IS_REQUEST
 832      &&
 833      isToplevelProgress
 834      &&
 835      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
 836  {
 837    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 838           ("SecureUI:%p: OnStateChange: SOMETHING STOPS FOR TOPMOST DOCUMENT\n", this));
 839  }
 840#endif
 841
 842  bool isSubDocumentRelevant = true;
 843
 844  // We are only interested in requests that load in the browser window...
 845  nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(aRequest));
 846  if (imgRequest) {
 847    // for image requests, we get the URI from here
 848    imgRequest->GetURI(getter_AddRefs(uri));
 849  } else { // is not imgRequest
 850    nsCOMPtr<nsIHttpChannel> httpRequest(do_QueryInterface(aRequest));
 851    if (!httpRequest) {
 852      nsCOMPtr<nsIFileChannel> fileRequest(do_QueryInterface(aRequest));
 853      if (!fileRequest) {
 854        nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
 855        if (!wyciwygRequest) {
 856          nsCOMPtr<nsIFTPChannel> ftpRequest(do_QueryInterface(aRequest));
 857          if (!ftpRequest) {
 858            PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 859                   ("SecureUI:%p: OnStateChange: not relevant for sub content\n", this));
 860            isSubDocumentRelevant = false;
 861          }
 862        }
 863      }
 864    }
 865  }
 866
 867  // This will ignore all resource, chrome, data, file, moz-icon, and anno
 868  // protocols. Local resources are treated as trusted.
 869  if (uri && ioService) {
 870    bool hasFlag;
 871    nsresult rv = 
 872      ioService->URIChainHasFlags(uri, 
 873                                  nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
 874                                  &hasFlag);
 875    if (NS_SUCCEEDED(rv) && hasFlag) {
 876      isSubDocumentRelevant = false;
 877    }
 878  }
 879
 880#if defined(DEBUG)
 881  nsCString info2;
 882  PRUint32 testFlags = loadFlags;
 883
 884  if (testFlags & nsIChannel::LOAD_DOCUMENT_URI)
 885  {
 886    testFlags -= nsIChannel::LOAD_DOCUMENT_URI;
 887    info2.Append("LOAD_DOCUMENT_URI ");
 888  }
 889  if (testFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
 890  {
 891    testFlags -= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
 892    info2.Append("LOAD_RETARGETED_DOCUMENT_URI ");
 893  }
 894  if (testFlags & nsIChannel::LOAD_REPLACE)
 895  {
 896    testFlags -= nsIChannel::LOAD_REPLACE;
 897    info2.Append("LOAD_REPLACE ");
 898  }
 899
 900  const char *_status = NS_SUCCEEDED(aStatus) ? "1" : "0";
 901
 902  nsCString info;
 903  PRUint32 f = aProgressStateFlags;
 904  if (f & nsIWebProgressListener::STATE_START)
 905  {
 906    f -= nsIWebProgressListener::STATE_START;
 907    info.Append("START ");
 908  }
 909  if (f & nsIWebProgressListener::STATE_REDIRECTING)
 910  {
 911    f -= nsIWebProgressListener::STATE_REDIRECTING;
 912    info.Append("REDIRECTING ");
 913  }
 914  if (f & nsIWebProgressListener::STATE_TRANSFERRING)
 915  {
 916    f -= nsIWebProgressListener::STATE_TRANSFERRING;
 917    info.Append("TRANSFERRING ");
 918  }
 919  if (f & nsIWebProgressListener::STATE_NEGOTIATING)
 920  {
 921    f -= nsIWebProgressListener::STATE_NEGOTIATING;
 922    info.Append("NEGOTIATING ");
 923  }
 924  if (f & nsIWebProgressListener::STATE_STOP)
 925  {
 926    f -= nsIWebProgressListener::STATE_STOP;
 927    info.Append("STOP ");
 928  }
 929  if (f & nsIWebProgressListener::STATE_IS_REQUEST)
 930  {
 931    f -= nsIWebProgressListener::STATE_IS_REQUEST;
 932    info.Append("IS_REQUEST ");
 933  }
 934  if (f & nsIWebProgressListener::STATE_IS_DOCUMENT)
 935  {
 936    f -= nsIWebProgressListener::STATE_IS_DOCUMENT;
 937    info.Append("IS_DOCUMENT ");
 938  }
 939  if (f & nsIWebProgressListener::STATE_IS_NETWORK)
 940  {
 941    f -= nsIWebProgressListener::STATE_IS_NETWORK;
 942    info.Append("IS_NETWORK ");
 943  }
 944  if (f & nsIWebProgressListener::STATE_IS_WINDOW)
 945  {
 946    f -= nsIWebProgressListener::STATE_IS_WINDOW;
 947    info.Append("IS_WINDOW ");
 948  }
 949  if (f & nsIWebProgressListener::STATE_IS_INSECURE)
 950  {
 951    f -= nsIWebProgressListener::STATE_IS_INSECURE;
 952    info.Append("IS_INSECURE ");
 953  }
 954  if (f & nsIWebProgressListener::STATE_IS_BROKEN)
 955  {
 956    f -= nsIWebProgressListener::STATE_IS_BROKEN;
 957    info.Append("IS_BROKEN ");
 958  }
 959  if (f & nsIWebProgressListener::STATE_IS_SECURE)
 960  {
 961    f -= nsIWebProgressListener::STATE_IS_SECURE;
 962    info.Append("IS_SECURE ");
 963  }
 964  if (f & nsIWebProgressListener::STATE_SECURE_HIGH)
 965  {
 966    f -= nsIWebProgressListener::STATE_SECURE_HIGH;
 967    info.Append("SECURE_HIGH ");
 968  }
 969  if (f & nsIWebProgressListener::STATE_SECURE_MED)
 970  {
 971    f -= nsIWebProgressListener::STATE_SECURE_MED;
 972    info.Append("SECURE_MED ");
 973  }
 974  if (f & nsIWebProgressListener::STATE_SECURE_LOW)
 975  {
 976    f -= nsIWebProgressListener::STATE_SECURE_LOW;
 977    info.Append("SECURE_LOW ");
 978  }
 979  if (f & nsIWebProgressListener::STATE_RESTORING)
 980  {
 981    f -= nsIWebProgressListener::STATE_RESTORING;
 982    info.Append("STATE_RESTORING ");
 983  }
 984
 985  if (f > 0)
 986  {
 987    info.Append("f contains unknown flag!");
 988  }
 989
 990  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 991         ("SecureUI:%p: OnStateChange: %s %s -- %s\n", this, _status, 
 992          info.get(), info2.get()));
 993
 994  if (aProgressStateFlags & STATE_STOP
 995      &&
 996      channel)
 997  {
 998    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
 999           ("SecureUI:%p: OnStateChange: seeing STOP with security state: %d\n", this,
1000            GetSecurityStateFromSecurityInfo(securityInfo)
1001            ));
1002  }
1003#endif
1004
1005  if (aProgressStateFlags & STATE_TRANSFERRING
1006      &&
1007      aProgressStateFlags & STATE_IS_REQUEST)
1008  {
1009    // The listing of a request in mTransferringRequests
1010    // means, there has already been data transfered.
1011
1012    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1013    PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_ADD);
1014    
1015    return NS_OK;
1016  }
1017
1018  bool requestHasTransferedData = false;
1019
1020  if (aProgressStateFlags & STATE_STOP
1021      &&
1022      aProgressStateFlags & STATE_IS_REQUEST)
1023  {
1024    { /* scope for the ReentrantMonitorAutoEnter */
1025      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1026      PLDHashEntryHdr *entry = PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_LOOKUP);
1027      if (PL_DHASH_ENTRY_IS_BUSY(entry))
1028      {
1029        PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_REMOVE);
1030
1031        requestHasTransferedData = true;
1032      }
1033    }
1034
1035    if (!requestHasTransferedData) {
1036      // Because image loads doesn't support any TRANSFERRING notifications but
1037      // only START and STOP we must ask them directly whether content was
1038      // transferred.  See bug 432685 for details.
1039      nsCOMPtr<nsISecurityInfoProvider> securityInfoProvider =
1040        do_QueryInterface(aRequest);
1041      // Guess true in all failure cases to be safe.  But if we're not
1042      // an nsISecurityInfoProvider, then we just haven't transferred
1043      // any data.
1044      bool hasTransferred;
1045      requestHasTransferedData =
1046        securityInfoProvider &&
1047        (NS_FAILED(securityInfoProvider->GetHasTransferredData(&hasTransferred)) ||
1048         hasTransferred);
1049    }
1050  }
1051
1052  bool allowSecurityStateChange = true;
1053  if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
1054  {
1055    // The original consumer (this) is no longer the target of the load.
1056    // Ignore any events with this flag, do not allow them to update
1057    // our secure UI state.
1058    allowSecurityStateChange = false;
1059  }
1060
1061  if (aProgressStateFlags & STATE_START
1062      &&
1063      aProgressStateFlags & STATE_IS_REQUEST
1064      &&
1065      isToplevelProgress
1066      &&
1067      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
1068  {
1069    bool inProgress;
1070
1071    PRInt32 saveSubHigh;
1072    PRInt32 saveSubLow;
1073    PRInt32 saveSubBroken;
1074    PRInt32 saveSubNo;
1075    nsCOMPtr<nsIAssociatedContentSecurity> prevContentSecurity;
1076
1077    PRInt32 newSubHigh = 0;
1078    PRInt32 newSubLow = 0;
1079    PRInt32 newSubBroken = 0;
1080    PRInt32 newSubNo = 0;
1081
1082    {
1083      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1084      inProgress = (mDocumentRequestsInProgress!=0);
1085
1086      if (allowSecurityStateChange && !inProgress)
1087      {
1088        saveSubHigh = mSubRequestsHighSecurity;
1089        saveSubLow = mSubRequestsLowSecurity;
1090        saveSubBroken = mSubRequestsBrokenSecurity;
1091        saveSubNo = mSubRequestsNoSecurity;
1092        prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
1093      }
1094    }
1095
1096    if (allowSecurityStateChange && !inProgress)
1097    {
1098      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1099             ("SecureUI:%p: OnStateChange: start for toplevel document\n", this
1100              ));
1101
1102      if (prevContentSecurity)
1103      {
1104        PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1105               ("SecureUI:%p: OnStateChange: start, saving current sub state\n", this
1106                ));
1107  
1108        // before resetting our state, let's save information about
1109        // sub element loads, so we can restore it later
1110        prevContentSecurity->SetCountSubRequestsHighSecurity(saveSubHigh);
1111        prevContentSecurity->SetCountSubRequestsLowSecurity(saveSubLow);
1112        prevContentSecurity->SetCountSubRequestsBrokenSecurity(saveSubBroken);
1113        prevContentSecurity->SetCountSubRequestsNoSecurity(saveSubNo);
1114        prevContentSecurity->Flush();
1115      }
1116
1117      bool retrieveAssociatedState = false;
1118
1119      if (securityInfo &&
1120          (aProgressStateFlags & nsIWebProgressListener::STATE_RESTORING) != 0) {
1121        retrieveAssociatedState = true;
1122      } else {
1123        nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
1124        if (wyciwygRequest) {
1125          retrieveAssociatedState = true;
1126        }
1127      }
1128
1129      if (retrieveAssociatedState)
1130      {
1131        // When restoring from bfcache, we will not get events for the 
1132        // page's sub elements, so let's load the state of sub elements
1133        // from the cache.
1134    
1135        nsCOMPtr<nsIAssociatedContentSecurity> 
1136          newContentSecurity(do_QueryInterface(securityInfo));
1137    
1138        if (newContentSecurity)
1139        {
1140          PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1141                 ("SecureUI:%p: OnStateChange: start, loading old sub state\n", this
1142                  ));
1143    
1144          newContentSecurity->GetCountSubRequestsHighSecurity(&newSubHigh);
1145          newContentSecurity->GetCountSubRequestsLowSecurity(&newSubLow);
1146          newContentSecurity->GetCountSubRequestsBrokenSecurity(&newSubBroken);
1147          newContentSecurity->GetCountSubRequestsNoSecurity(&newSubNo);
1148        }
1149      }
1150    }
1151
1152    {
1153      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1154
1155      if (allowSecurityStateChange && !inProgress)
1156      {
1157        ResetStateTracking();
1158        mSubRequestsHighSecurity = newSubHigh;
1159        mSubRequestsLowSecurity = newSubLow;
1160        mSubRequestsBrokenSecurity = newSubBroken;
1161        mSubRequestsNoSecurity = newSubNo;
1162        mNewToplevelSecurityStateKnown = false;
1163      }
1164
1165      // By using a counter, this code also works when the toplevel
1166      // document get's redirected, but the STOP request for the 
1167      // previous toplevel document has not yet have been received.
1168      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1169             ("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this
1170              ));
1171      ++mDocumentRequestsInProgress;
1172    }
1173
1174    return NS_OK;
1175  }
1176
1177  if (aProgressStateFlags & STATE_STOP
1178      &&
1179      aProgressStateFlags & STATE_IS_REQUEST
1180      &&
1181      isToplevelProgress
1182      &&
1183      loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
1184  {
1185    PRInt32 temp_DocumentRequestsInProgress;
1186    nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
1187
1188    {
1189      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1190      temp_DocumentRequestsInProgress = mDocumentRequestsInProgress;
1191      if (allowSecurityStateChange)
1192      {
1193        temp_ToplevelEventSink = mToplevelEventSink;
1194      }
1195    }
1196
1197    if (temp_DocumentRequestsInProgress <= 0)
1198    {
1199      // Ignore stop requests unless a document load is in progress
1200      // Unfortunately on application start, see some stops without having seen any starts...
1201      return NS_OK;
1202    }
1203
1204    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1205           ("SecureUI:%p: OnStateChange: --mDocumentRequestsInProgress\n", this
1206            ));
1207
1208    if (!temp_ToplevelEventSink && channel)
1209    {
1210      if (allowSecurityStateChange)
1211      {
1212        ObtainEventSink(channel, temp_ToplevelEventSink);
1213      }
1214    }
1215
1216    {
1217      ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1218      if (allowSecurityStateChange)
1219      {
1220        mToplevelEventSink = temp_ToplevelEventSink;
1221      }
1222      --mDocumentRequestsInProgress;
1223    }
1224
1225    if (allowSecurityStateChange && requestHasTransferedData) {
1226      // Data has been transferred for the single toplevel
1227      // request. Evaluate the security state.
1228
1229      return EvaluateAndUpdateSecurityState(aRequest, securityInfo, false);
1230    }
1231    
1232    return NS_OK;
1233  }
1234  
1235  if (aProgressStateFlags & STATE_STOP
1236      &&
1237      aProgressStateFlags & STATE_IS_REQUEST)
1238  {
1239    if (!isSubDocumentRelevant)
1240      return NS_OK;
1241    
1242    // if we arrive here, LOAD_DOCUMENT_URI is not set
1243    
1244    // We only care for the security state of sub requests which have actually transfered data.
1245
1246    if (allowSecurityStateChange && requestHasTransferedData)
1247    {  
1248      UpdateSubrequestMembers(securityInfo);
1249      
1250      // Care for the following scenario:
1251      // A new top level document load might have already started,
1252      // but the security state of the new top level document might not yet been known.
1253      // 
1254      // At this point, we are learning about the security state of a sub-document.
1255      // We must not update the security state based on the sub content,
1256      // if the new top level state is not yet known.
1257      //
1258      // We skip updating the security state in this case.
1259
1260      bool temp_NewToplevelSecurityStateKnown;
1261      {
1262        ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1263        temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown;
1264      }
1265
1266      if (temp_NewToplevelSecurityStateKnown)
1267        return UpdateSecurityState(aRequest, false, false, false);
1268    }
1269
1270    return NS_OK;
1271  }
1272
1273  return NS_OK;
1274}
1275
1276// I'm keeping this as a separate function, in order to simplify the review
1277// for bug 412456. We should inline this in a follow up patch.
1278void nsSecureBrowserUIImpl::ObtainEventSink(nsIChannel *channel, 
1279                                            nsCOMPtr<nsISecurityEventSink> &sink)
1280{
1281  if (!sink)
1282    NS_QueryNotificationCallbacks(channel, sink);
1283}
1284
1285nsresult nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest, 
1286                                                    bool withNewLocation, 
1287                                                    bool withUpdateStatus, 
1288                                                    bool withUpdateTooltip)
1289{
1290  lockIconState warnSecurityState = lis_no_security;
1291  bool showWarning = false;
1292  nsresult rv = NS_OK;
1293
1294  // both parameters are both input and outout
1295  bool flagsChanged = UpdateMyFlags(showWarning, warnSecurityState);
1296
1297  if (flagsChanged || withNewLocation || withUpdateStatus || withUpdateTooltip)
1298    rv = TellTheWorld(showWarning, warnSecurityState, aRequest);
1299
1300  return rv;
1301}
1302
1303// must not fail, by definition, only trivial assignments
1304// or string operations are allowed
1305// returns true if our overall state has changed and we must send out notifications
1306bool nsSecureBrowserUIImpl::UpdateMyFlags(bool &showWarning, lockIconState &warnSecurityState)
1307{
1308  ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1309  bool mustTellTheWorld = false;
1310
1311  lockIconState newSecurityState;
1312
1313  if (mNewToplevelSecurityState & STATE_IS_SECURE)
1314  {
1315    if (mNewToplevelSecurityState & STATE_SECURE_LOW
1316        ||
1317        mNewToplevelSecurityState & STATE_SECURE_MED)
1318    {
1319      if (mSubRequestsBrokenSecurity
1320          ||
1321          mSubRequestsNoSecurity)
1322      {
1323        newSecurityState = lis_mixed_security;
1324      }
1325      else
1326      {
1327        newSecurityState = lis_low_security;
1328      }
1329    }
1330    else
1331    {
1332      // toplevel is high security
1333
1334      if (mSubRequestsBrokenSecurity
1335          ||
1336          mSubRequestsNoSecurity)
1337      {
1338        newSecurityState = lis_mixed_security;
1339      }
1340      else if (mSubRequestsLowSecurity)
1341      {
1342        newSecurityState = lis_low_security;
1343      }
1344      else
1345      {
1346        newSecurityState = lis_high_security;
1347      }
1348    }
1349  }
1350  else
1351  if (mNewToplevelSecurityState & STATE_IS_BROKEN)
1352  {
1353    // indicating BROKEN is more important than MIXED.
1354  
1355    newSecurityState = lis_broken_security;
1356  }
1357  else
1358  {
1359    newSecurityState = lis_no_security;
1360  }
1361
1362  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1363         ("SecureUI:%p: UpdateSecurityState:  old-new  %d - %d\n", this,
1364         mNotifiedSecurityState, newSecurityState
1365          ));
1366
1367  if (mNotifiedSecurityState != newSecurityState)
1368  {
1369    mustTellTheWorld = true;
1370
1371    // we'll treat "broken" exactly like "insecure",
1372    // i.e. we do not show alerts when switching between broken and insecure
1373
1374    /*
1375      from                 to           shows alert
1376    ------------------------------     ---------------
1377
1378    no or broken -> no or broken    => <NOTHING SHOWN>
1379
1380    no or broken -> mixed           => mixed alert
1381    no or broken -> low             => low alert
1382    no or broken -> high            => high alert
1383    
1384    mixed, high, low -> no, broken  => leaving secure
1385
1386    mixed        -> low             => low alert
1387    mixed        -> high            => high alert
1388
1389    high         -> low             => low alert
1390    high         -> mixed           => mixed
1391    
1392    low          -> high            => high
1393    low          -> mixed           => mixed
1394
1395
1396      security    icon
1397      ----------------
1398    
1399      no          open
1400      mixed       broken
1401      broken      broken
1402      low         low
1403      high        high
1404    */
1405
1406    showWarning = true;
1407    
1408    switch (mNotifiedSecurityState)
1409    {
1410      case lis_no_security:
1411      case lis_broken_security:
1412        switch (newSecurityState)
1413        {
1414          case lis_no_security:
1415          case lis_broken_security:
1416            showWarning = false;
1417            break;
1418          
1419          default:
1420            break;
1421        }
1422      
1423      default:
1424        break;
1425    }
1426
1427    if (showWarning)
1428    {
1429      warnSecurityState = newSecurityState;
1430    }
1431    
1432    mNotifiedSecurityState = newSecurityState;
1433
1434    if (lis_no_security == newSecurityState)
1435    {
1436      mSSLStatus = nsnull;
1437      mInfoTooltip.Truncate();
1438    }
1439  }
1440
1441  if (mNotifiedToplevelIsEV != mNewToplevelIsEV) {
1442    mustTellTheWorld = true;
1443    mNotifiedToplevelIsEV = mNewToplevelIsEV;
1444  }
1445
1446  return mustTellTheWorld;
1447}
1448
1449nsresult nsSecureBrowserUIImpl::TellTheWorld(bool showWarning, 
1450                                             lockIconState warnSecurityState, 
1451                                             nsIRequest* aRequest)
1452{
1453  nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
1454  lockIconState temp_NotifiedSecurityState;
1455  bool temp_NotifiedToplevelIsEV;
1456
1457  {
1458    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1459    temp_ToplevelEventSink = mToplevelEventSink;
1460    temp_NotifiedSecurityState = mNotifiedSecurityState;
1461    temp_NotifiedToplevelIsEV = mNotifiedToplevelIsEV;
1462  }
1463
1464  if (temp_ToplevelEventSink)
1465  {
1466    PRUint32 newState = STATE_IS_INSECURE;
1467    MapInternalToExternalState(&newState, 
1468                               temp_NotifiedSecurityState, 
1469                               temp_NotifiedToplevelIsEV);
1470
1471    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1472           ("SecureUI:%p: UpdateSecurityState: calling OnSecurityChange\n", this
1473            ));
1474
1475    temp_ToplevelEventSink->OnSecurityChange(aRequest, newState);
1476  }
1477  else
1478  {
1479    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1480           ("SecureUI:%p: UpdateSecurityState: NO mToplevelEventSink!\n", this
1481            ));
1482
1483  }
1484
1485  if (showWarning)
1486  {
1487    switch (warnSecurityState)
1488    {
1489      case lis_no_security:
1490      case lis_broken_security:
1491        ConfirmLeavingSecure();
1492        break;
1493
1494      case lis_mixed_security:
1495        ConfirmMixedMode();
1496        break;
1497
1498      case lis_low_security:
1499        ConfirmEnteringWeak();
1500        break;
1501
1502      case lis_high_security:
1503        ConfirmEnteringSecure();
1504        break;
1505    }
1506  }
1507
1508  return NS_OK; 
1509}
1510
1511NS_IMETHODIMP
1512nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
1513                                        nsIRequest* aRequest,
1514                                        nsIURI* aLocation,
1515                                        PRUint32 aFlags)
1516{
1517#ifdef DEBUG
1518  nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
1519  NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
1520               "unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
1521#endif
1522  PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1523         ("SecureUI:%p: OnLocationChange\n", this));
1524
1525  bool updateIsViewSource = false;
1526  bool temp_IsViewSource = false;
1527  nsCOMPtr<nsIDOMWindow> window;
1528
1529  if (aLocation)
1530  {
1531    bool vs;
1532
1533    nsresult rv = aLocation->SchemeIs("view-source", &vs);
1534    NS_ENSURE_SUCCESS(rv, rv);
1535
1536    if (vs) {
1537      PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1538             ("SecureUI:%p: OnLocationChange: view-source\n", this));
1539    }
1540
1541    updateIsViewSource = true;
1542    temp_IsViewSource = vs;
1543  }
1544
1545  {
1546    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1547    if (updateIsViewSource) {
1548      mIsViewSource = temp_IsViewSource;
1549    }
1550    mCurrentURI = aLocation;
1551    window = do_QueryReferent(mWindow);
1552    NS_ASSERTION(window, "Window has gone away?!");
1553  }
1554
1555  // When |aRequest| is null, basically we don't trust that document. But if
1556  // docshell insists that the document has not changed at all, we will reuse
1557  // the previous security state, no matter what |aRequest| may be.
1558  if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT)
1559    return NS_OK;
1560
1561  // The location bar has changed, so we must update the security state.  The
1562  // only concern with doing this here is that a page may transition from being
1563  // reported as completely secure to being reported as partially secure
1564  // (mixed).  This may be confusing for users, and it may bother users who
1565  // like seeing security dialogs.  However, it seems prudent given that page
1566  // loading may never end in some edge cases (perhaps by a site with malicious
1567  // intent).
1568
1569  nsCOMPtr<nsIDOMWindow> windowForProgress;
1570  aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
1571
1572  nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
1573
1574  if (windowForProgress.get() == window.get()) {
1575    // For toplevel channels, update the security state right away.
1576    return EvaluateAndUpdateSecurityState(aRequest, securityInfo, true);
1577  }
1578
1579  // For channels in subdocuments we only update our subrequest state members.
1580  UpdateSubrequestMembers(securityInfo);
1581
1582  // Care for the following scenario:
1583
1584  // A new toplevel document load might have already started, but the security
1585  // state of the new toplevel document might not yet be known.
1586  // 
1587  // At this point, we are learning about the security state of a sub-document.
1588  // We must not update the security state based on the sub content, if the new
1589  // top level state is not yet known.
1590  //
1591  // We skip updating the security state in this case.
1592
1593  bool temp_NewToplevelSecurityStateKnown;
1594  {
1595    ReentrantMonitorAutoEnter lock(mReentrantMonitor);
1596    temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown;
1597  }
1598
1599  if (temp_NewToplevelSecurityStateKnown)
1600    return UpdateSecurityState(aRequest, true, false, false);
1601
1602  return NS_OK;
1603}
1604
1605NS_IMETHODIMP
1606nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress* aWebProgress,
1607                                      nsIRequest* aRequest,
1608                                      nsresult aStatus,
1609                                      const PRUnichar* aMessage)
1610{
1611  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
1612  return NS_OK;
1613}
1614
1615nsresult
1616nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress *aWebProgress,
1617                                        nsIRequest *aRequest,
1618                                        PRUint32 state)
1619{
1620#if defined(DEBUG)
1621  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
1622  if (!channel)
1623    return NS_OK;
1624
1625  nsCOMPtr<nsIURI> aURI;
1626  channel->GetURI(getter_AddRefs(aURI));
1627  
1628  if (aURI) {
1629    nsCAutoString temp;
1630    aURI->GetSpec(temp);
1631    PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
1632           ("SecureU…

Large files files are truncated, but you can click here to view the full file