security/manager/boot/src/nsSecureBrowserUIImpl.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #ifdef MOZ_LOGGING
     7 #define FORCE_PR_LOG
     8 #endif
    10 #include "nspr.h"
    11 #include "prlog.h"
    13 #include "nsISecureBrowserUI.h"
    14 #include "nsSecureBrowserUIImpl.h"
    15 #include "nsCOMPtr.h"
    16 #include "nsIInterfaceRequestor.h"
    17 #include "nsIInterfaceRequestorUtils.h"
    18 #include "nsIServiceManager.h"
    19 #include "nsCURILoader.h"
    20 #include "nsIDocShell.h"
    21 #include "nsIDocShellTreeItem.h"
    22 #include "nsIDocument.h"
    23 #include "nsIPrincipal.h"
    24 #include "nsIDOMElement.h"
    25 #include "nsPIDOMWindow.h"
    26 #include "nsIContent.h"
    27 #include "nsIWebProgress.h"
    28 #include "nsIWebProgressListener.h"
    29 #include "nsIChannel.h"
    30 #include "nsIHttpChannel.h"
    31 #include "nsIFileChannel.h"
    32 #include "nsIWyciwygChannel.h"
    33 #include "nsIFTPChannel.h"
    34 #include "nsITransportSecurityInfo.h"
    35 #include "nsISSLStatus.h"
    36 #include "nsIURI.h"
    37 #include "nsISecurityEventSink.h"
    38 #include "nsIPrompt.h"
    39 #include "nsIFormSubmitObserver.h"
    40 #include "nsISecurityWarningDialogs.h"
    41 #include "nsISecurityInfoProvider.h"
    42 #include "imgIRequest.h"
    43 #include "nsThreadUtils.h"
    44 #include "nsNetUtil.h"
    45 #include "nsNetCID.h"
    46 #include "nsCRT.h"
    48 using namespace mozilla;
    50 #define IS_SECURE(state) ((state & 0xFFFF) == STATE_IS_SECURE)
    52 #if defined(PR_LOGGING)
    53 //
    54 // Log module for nsSecureBrowserUI logging...
    55 //
    56 // To enable logging (see prlog.h for full details):
    57 //
    58 //    set NSPR_LOG_MODULES=nsSecureBrowserUI:5
    59 //    set NSPR_LOG_FILE=nspr.log
    60 //
    61 // this enables PR_LOG_DEBUG level information and places all output in
    62 // the file nspr.log
    63 //
    64 PRLogModuleInfo* gSecureDocLog = nullptr;
    65 #endif /* PR_LOGGING */
    67 struct RequestHashEntry : PLDHashEntryHdr {
    68     void *r;
    69 };
    71 static bool
    72 RequestMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
    73                          const void *key)
    74 {
    75   const RequestHashEntry *entry = static_cast<const RequestHashEntry*>(hdr);
    76   return entry->r == key;
    77 }
    79 static bool
    80 RequestMapInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
    81                      const void *key)
    82 {
    83   RequestHashEntry *entry = static_cast<RequestHashEntry*>(hdr);
    84   entry->r = (void*)key;
    85   return true;
    86 }
    88 static const PLDHashTableOps gMapOps = {
    89   PL_DHashAllocTable,
    90   PL_DHashFreeTable,
    91   PL_DHashVoidPtrKeyStub,
    92   RequestMapMatchEntry,
    93   PL_DHashMoveEntryStub,
    94   PL_DHashClearEntryStub,
    95   PL_DHashFinalizeStub,
    96   RequestMapInitEntry
    97 };
    99 #ifdef DEBUG
   100 class nsAutoAtomic {
   101   public:
   102     nsAutoAtomic(Atomic<int32_t> &i)
   103     :mI(i) {
   104       mI++;
   105     }
   107     ~nsAutoAtomic() {
   108       mI--;
   109     }
   111   protected:
   112     Atomic<int32_t> &mI;
   114   private:
   115     nsAutoAtomic(); // not accessible
   116 };
   117 #endif
   119 nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
   120   : mReentrantMonitor("nsSecureBrowserUIImpl.mReentrantMonitor")
   121   , mNotifiedSecurityState(lis_no_security)
   122   , mNotifiedToplevelIsEV(false)
   123   , mNewToplevelSecurityState(STATE_IS_INSECURE)
   124   , mNewToplevelIsEV(false)
   125   , mNewToplevelSecurityStateKnown(true)
   126   , mIsViewSource(false)
   127   , mSubRequestsBrokenSecurity(0)
   128   , mSubRequestsNoSecurity(0)
   129   , mRestoreSubrequests(false)
   130   , mOnLocationChangeSeen(false)
   131 #ifdef DEBUG
   132   , mOnStateLocationChangeReentranceDetection(0)
   133 #endif
   134 {
   135   mTransferringRequests.ops = nullptr;
   136   ResetStateTracking();
   138 #if defined(PR_LOGGING)
   139   if (!gSecureDocLog)
   140     gSecureDocLog = PR_NewLogModule("nsSecureBrowserUI");
   141 #endif /* PR_LOGGING */
   142 }
   144 nsSecureBrowserUIImpl::~nsSecureBrowserUIImpl()
   145 {
   146   if (mTransferringRequests.ops) {
   147     PL_DHashTableFinish(&mTransferringRequests);
   148     mTransferringRequests.ops = nullptr;
   149   }
   150 }
   152 NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
   153                   nsISecureBrowserUI,
   154                   nsIWebProgressListener,
   155                   nsIFormSubmitObserver,
   156                   nsISupportsWeakReference,
   157                   nsISSLStatusProvider)
   159 NS_IMETHODIMP
   160 nsSecureBrowserUIImpl::Init(nsIDOMWindow *aWindow)
   161 {
   163 #ifdef PR_LOGGING
   164   nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
   166   PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   167          ("SecureUI:%p: Init: mWindow: %p, aWindow: %p\n", this,
   168           window.get(), aWindow));
   169 #endif
   171   if (!aWindow) {
   172     NS_WARNING("Null window passed to nsSecureBrowserUIImpl::Init()");
   173     return NS_ERROR_INVALID_ARG;
   174   }
   176   if (mWindow) {
   177     NS_WARNING("Trying to init an nsSecureBrowserUIImpl twice");
   178     return NS_ERROR_ALREADY_INITIALIZED;
   179   }
   181   nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(aWindow));
   182   if (pwin->IsInnerWindow()) {
   183     pwin = pwin->GetOuterWindow();
   184   }
   186   nsresult rv;
   187   mWindow = do_GetWeakReference(pwin, &rv);
   188   NS_ENSURE_SUCCESS(rv, rv);
   190   nsCOMPtr<nsPIDOMWindow> piwindow(do_QueryInterface(aWindow));
   191   if (!piwindow) return NS_ERROR_FAILURE;
   193   nsIDocShell *docShell = piwindow->GetDocShell();
   195   // The Docshell will own the SecureBrowserUI object
   196   if (!docShell)
   197     return NS_ERROR_FAILURE;
   199   docShell->SetSecurityUI(this);
   201   /* GetWebProgress(mWindow) */
   202   // hook up to the webprogress notifications.
   203   nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
   204   if (!wp) return NS_ERROR_FAILURE;
   205   /* end GetWebProgress */
   207   wp->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
   208                           nsIWebProgress::NOTIFY_STATE_ALL | 
   209                           nsIWebProgress::NOTIFY_LOCATION  |
   210                           nsIWebProgress::NOTIFY_SECURITY);
   213   return NS_OK;
   214 }
   216 NS_IMETHODIMP
   217 nsSecureBrowserUIImpl::GetState(uint32_t* aState)
   218 {
   219   ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   220   return MapInternalToExternalState(aState, mNotifiedSecurityState, mNotifiedToplevelIsEV);
   221 }
   223 // static
   224 already_AddRefed<nsISupports> 
   225 nsSecureBrowserUIImpl::ExtractSecurityInfo(nsIRequest* aRequest)
   226 {
   227   nsCOMPtr<nsISupports> retval;
   228   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   229   if (channel)
   230     channel->GetSecurityInfo(getter_AddRefs(retval));
   232   if (!retval) {
   233     nsCOMPtr<nsISecurityInfoProvider> provider(do_QueryInterface(aRequest));
   234     if (provider)
   235       provider->GetSecurityInfo(getter_AddRefs(retval));
   236   }
   238   return retval.forget();
   239 }
   241 nsresult
   242 nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconState lock, bool ev)
   243 {
   244   NS_ENSURE_ARG(aState);
   246   switch (lock)
   247   {
   248     case lis_broken_security:
   249       *aState = STATE_IS_BROKEN;
   250       break;
   252     case lis_mixed_security:
   253       *aState = STATE_IS_BROKEN;
   254       break;
   256     case lis_high_security:
   257       *aState = STATE_IS_SECURE | STATE_SECURE_HIGH;
   258       break;
   260     default:
   261     case lis_no_security:
   262       *aState = STATE_IS_INSECURE;
   263       break;
   264   }
   266   if (ev && (*aState & STATE_IS_SECURE))
   267     *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
   269   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
   270   if (!docShell)
   271     return NS_OK;
   273   // For content docShell's, the mixed content security state is set on the root docShell.
   274   if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
   275     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(docShell));
   276     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
   277     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
   278     NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
   279     docShell = do_QueryInterface(sameTypeRoot);
   280     if (!docShell)
   281       return NS_OK;
   282   }
   284   // Has a Mixed Content Load initiated in nsMixedContentBlocker?
   285   // If so, the state should be broken; overriding the previous state
   286   // set by the lock parameter.
   287   if (docShell->GetHasMixedActiveContentLoaded() &&
   288       docShell->GetHasMixedDisplayContentLoaded()) {
   289       *aState = STATE_IS_BROKEN |
   290                 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
   291                 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
   292   } else if (docShell->GetHasMixedActiveContentLoaded()) {
   293       *aState = STATE_IS_BROKEN |
   294                 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
   295   } else if (docShell->GetHasMixedDisplayContentLoaded()) {
   296       *aState = STATE_IS_BROKEN |
   297                 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
   298   }
   300   // Has Mixed Content Been Blocked in nsMixedContentBlocker?
   301   if (docShell->GetHasMixedActiveContentBlocked())
   302     *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
   304   if (docShell->GetHasMixedDisplayContentBlocked())
   305     *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
   307   return NS_OK;
   308 }
   310 NS_IMETHODIMP
   311 nsSecureBrowserUIImpl::SetDocShell(nsIDocShell *aDocShell)
   312 {
   313   nsresult rv;
   314   mDocShell = do_GetWeakReference(aDocShell, &rv);
   315   return rv;
   316 }
   318 static nsresult IsChildOfDomWindow(nsIDOMWindow *parent, nsIDOMWindow *child,
   319                                    bool* value)
   320 {
   321   *value = false;
   323   if (parent == child) {
   324     *value = true;
   325     return NS_OK;
   326   }
   328   nsCOMPtr<nsIDOMWindow> childsParent;
   329   child->GetParent(getter_AddRefs(childsParent));
   331   if (childsParent && childsParent.get() != child)
   332     IsChildOfDomWindow(parent, childsParent, value);
   334   return NS_OK;
   335 }
   337 static uint32_t GetSecurityStateFromSecurityInfo(nsISupports *info)
   338 {
   339   nsresult res;
   340   uint32_t securityState;
   342   nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
   343   if (!psmInfo) {
   344     PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - no nsITransportSecurityInfo for %p\n",
   345                                          (nsISupports *)info));
   346     return nsIWebProgressListener::STATE_IS_INSECURE;
   347   }
   348   PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - info is %p\n", 
   349                                        (nsISupports *)info));
   351   res = psmInfo->GetSecurityState(&securityState);
   352   if (NS_FAILED(res)) {
   353     PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - GetSecurityState failed: %d\n",
   354                                          res));
   355     securityState = nsIWebProgressListener::STATE_IS_BROKEN;
   356   }
   358   PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI: GetSecurityState: - Returning %d\n", 
   359                                        securityState));
   360   return securityState;
   361 }
   364 NS_IMETHODIMP
   365 nsSecureBrowserUIImpl::Notify(nsIDOMHTMLFormElement* aDOMForm,
   366                               nsIDOMWindow* aWindow, nsIURI* actionURL,
   367                               bool* cancelSubmit)
   368 {
   369   // Return NS_OK unless we want to prevent this form from submitting.
   370   *cancelSubmit = false;
   371   if (!aWindow || !actionURL || !aDOMForm)
   372     return NS_OK;
   374   nsCOMPtr<nsIContent> formNode = do_QueryInterface(aDOMForm);
   376   nsCOMPtr<nsIDocument> document = formNode->GetDocument();
   377   if (!document) return NS_OK;
   379   nsIPrincipal *principal = formNode->NodePrincipal();
   381   if (!principal)
   382   {
   383     *cancelSubmit = true;
   384     return NS_OK;
   385   }
   387   nsCOMPtr<nsIURI> formURL;
   388   if (NS_FAILED(principal->GetURI(getter_AddRefs(formURL))) ||
   389       !formURL)
   390   {
   391     formURL = document->GetDocumentURI();
   392   }
   394   nsCOMPtr<nsIDOMWindow> postingWindow =
   395     do_QueryInterface(document->GetWindow());
   396   // We can't find this document's window, cancel it.
   397   if (!postingWindow)
   398   {
   399     NS_WARNING("If you see this and can explain why it should be allowed, note in Bug 332324");
   400     *cancelSubmit = true;
   401     return NS_OK;
   402   }
   404   nsCOMPtr<nsIDOMWindow> window;
   405   {
   406     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   407     window = do_QueryReferent(mWindow);
   409     // The window was destroyed, so we assume no form was submitted within it.
   410     if (!window)
   411       return NS_OK;
   412   }
   414   bool isChild;
   415   IsChildOfDomWindow(window, postingWindow, &isChild);
   417   // This notify call is not for our window, ignore it.
   418   if (!isChild)
   419     return NS_OK;
   421   bool okayToPost;
   422   nsresult res = CheckPost(formURL, actionURL, &okayToPost);
   424   if (NS_SUCCEEDED(res) && !okayToPost)
   425     *cancelSubmit = true;
   427   return res;
   428 }
   430 //  nsIWebProgressListener
   431 NS_IMETHODIMP 
   432 nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress* aWebProgress,
   433                                         nsIRequest* aRequest,
   434                                         int32_t aCurSelfProgress,
   435                                         int32_t aMaxSelfProgress,
   436                                         int32_t aCurTotalProgress,
   437                                         int32_t aMaxTotalProgress)
   438 {
   439   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   440   return NS_OK;
   441 }
   443 void nsSecureBrowserUIImpl::ResetStateTracking()
   444 {
   445   ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   447   mDocumentRequestsInProgress = 0;
   448   if (mTransferringRequests.ops) {
   449     PL_DHashTableFinish(&mTransferringRequests);
   450     mTransferringRequests.ops = nullptr;
   451   }
   452   PL_DHashTableInit(&mTransferringRequests, &gMapOps, nullptr,
   453                     sizeof(RequestHashEntry), 16);
   454 }
   456 nsresult
   457 nsSecureBrowserUIImpl::EvaluateAndUpdateSecurityState(nsIRequest* aRequest, nsISupports *info,
   458                                                       bool withNewLocation)
   459 {
   460   /* I explicitly ignore the camelCase variable naming style here,
   461      I want to make it clear these are temp variables that relate to the 
   462      member variables with the same suffix.*/
   464   uint32_t temp_NewToplevelSecurityState = nsIWebProgressListener::STATE_IS_INSECURE;
   465   bool temp_NewToplevelIsEV = false;
   467   bool updateStatus = false;
   468   nsCOMPtr<nsISSLStatus> temp_SSLStatus;
   470     temp_NewToplevelSecurityState = GetSecurityStateFromSecurityInfo(info);
   472     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   473            ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n", this,
   474             temp_NewToplevelSecurityState));
   476     nsCOMPtr<nsISSLStatusProvider> sp = do_QueryInterface(info);
   477     if (sp) {
   478       // Ignore result
   479       updateStatus = true;
   480       (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
   481       if (temp_SSLStatus) {
   482         bool aTemp;
   483         if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) {
   484           temp_NewToplevelIsEV = aTemp;
   485         }
   486       }
   487     }
   489   // assume temp_NewToplevelSecurityState was set in this scope!
   490   // see code that is directly above
   492   {
   493     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   494     mNewToplevelSecurityStateKnown = true;
   495     mNewToplevelSecurityState = temp_NewToplevelSecurityState;
   496     mNewToplevelIsEV = temp_NewToplevelIsEV;
   497     if (updateStatus) {
   498       mSSLStatus = temp_SSLStatus;
   499     }
   500     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   501            ("SecureUI:%p: remember securityInfo %p\n", this,
   502             info));
   503     nsCOMPtr<nsIAssociatedContentSecurity> associatedContentSecurityFromRequest =
   504         do_QueryInterface(aRequest);
   505     if (associatedContentSecurityFromRequest)
   506         mCurrentToplevelSecurityInfo = aRequest;
   507     else
   508         mCurrentToplevelSecurityInfo = info;
   510     // The subrequest counters are now in sync with 
   511     // mCurrentToplevelSecurityInfo, don't restore after top level
   512     // document load finishes.
   513     mRestoreSubrequests = false;
   514   }
   516   return UpdateSecurityState(aRequest, withNewLocation, updateStatus);
   517 }
   519 void
   520 nsSecureBrowserUIImpl::UpdateSubrequestMembers(nsISupports *securityInfo)
   521 {
   522   // For wyciwyg channels in subdocuments we only update our
   523   // subrequest state members.
   524   uint32_t reqState = GetSecurityStateFromSecurityInfo(securityInfo);
   526   // the code above this line should run without a lock
   527   ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   529   if (reqState & STATE_IS_SECURE) {
   530     // do nothing
   531   } else if (reqState & STATE_IS_BROKEN) {
   532     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   533            ("SecureUI:%p: OnStateChange: subreq BROKEN\n", this));
   534     ++mSubRequestsBrokenSecurity;
   535   } else {
   536     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   537            ("SecureUI:%p: OnStateChange: subreq INSECURE\n", this));
   538     ++mSubRequestsNoSecurity;
   539   }
   540 }
   542 NS_IMETHODIMP
   543 nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
   544                                      nsIRequest* aRequest,
   545                                      uint32_t aProgressStateFlags,
   546                                      nsresult aStatus)
   547 {
   548 #ifdef DEBUG
   549   nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
   550   NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
   551                "unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
   552 #endif
   553   /*
   554     All discussion, unless otherwise mentioned, only refers to
   555     http, https, file or wyciwig requests.
   558     Redirects are evil, well, some of them.
   559     There are multiple forms of redirects.
   561     Redirects caused by http refresh content are ok, because experiments show,
   562     with those redirects, the old page contents and their requests will come to STOP
   563     completely, before any progress from new refreshed page content is reported.
   564     So we can safely treat them as separate page loading transactions.
   566     Evil are redirects at the http protocol level, like code 302.
   568     If the toplevel documents gets replaced, i.e. redirected with 302, we do not care for the 
   569     security state of the initial transaction, which has now been redirected, 
   570     we only care for the new page load.
   572     For the implementation of the security UI, we make an assumption, that is hopefully true.
   574     Imagine, the received page that was delivered with the 302 redirection answer,
   575     also delivered html content.
   577     What happens if the parser starts to analyze the content and tries to load contained sub objects?
   579     In that case we would see start and stop requests for subdocuments, some for the previous document,
   580     some for the new target document. And only those for the new toplevel document may be
   581     taken into consideration, when deciding about the security state of the next toplevel document.
   583     Because security state is being looked at, when loading stops for (sub)documents, this 
   584     could cause real confusion, because we have to decide, whether an incoming progress 
   585     belongs to the new toplevel page, or the previous, already redirected page.
   587     Can we simplify here?
   589     If a redirect at the http protocol level is seen, can we safely assume, its html content
   590     will not be parsed, anylzed, and no embedded objects will get loaded (css, js, images),
   591     because the redirect is already happening?
   593     If we can assume that, this really simplify things. Because we will never see notification
   594     for sub requests that need to get ignored.
   596     I would like to make this assumption for now, but please let me (kaie) know if I'm wrong.
   598     Excurse:
   599       If my assumption is wrong, then we would require more tracking information.
   600       We need to keep lists of all pointers to request object that had been seen since the
   601       last toplevel start event.
   602       If the start for a redirected page is seen, the list of releveant object must be cleared,
   603       and only progress for requests which start after it must be analyzed.
   604       All other events must be ignored, as they belong to now irrelevant previous top level documents.
   607     Frames are also evil.
   609     First we need a decision.
   610     kaie thinks: 
   611       Only if the toplevel frame is secure, we should try to display secure lock icons.
   612       If some of the inner contents are insecure, we display mixed mode.
   614       But if the top level frame is not secure, why indicate a mixed lock icon at all?
   615       I think we should always display an open lock icon, if the top level frameset is insecure.
   617       That's the way Netscape Communicator behaves, and I think we should do the same.
   619       The user will not know which parts are secure and which are not,
   620       and any certificate information, displayed in the tooltip or in the "page info"
   621       will only be relevant for some subframe(s), and the user will not know which ones,
   622       so we shouldn't display it as a general attribute of the displayed page.
   624     Why are frames evil?
   626     Because the progress for the toplevel frame document is not easily distinguishable
   627     from subframes. The same STATE bits are reported.
   629     While at first sight, when a new page load happens,
   630     the toplevel frameset document has also the STATE_IS_NETWORK bit in it.
   631     But this can't really be used. Because in case that document causes a http 302 redirect, 
   632     the real top level frameset will no longer have that bit.
   634     But we need some way to distinguish top level frames from inner frames.
   636     I saw that the web progress we get delivered has a reference to the toplevel DOM window.
   638     I suggest, we look at all incoming requests.
   639     If a request is NOT for the toplevel DOM window, we will always treat it as a subdocument request,
   640     regardless of whether the load flags indicate a top level document.
   641   */
   643   nsCOMPtr<nsIDOMWindow> windowForProgress;
   644   aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
   646   nsCOMPtr<nsIDOMWindow> window;
   647   bool isViewSource;
   649   nsCOMPtr<nsINetUtil> ioService;
   651   {
   652     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   653     window = do_QueryReferent(mWindow);
   654     NS_ASSERTION(window, "Window has gone away?!");
   655     isViewSource = mIsViewSource;
   656     ioService = mIOService;
   657   }
   659   if (!ioService)
   660   {
   661     ioService = do_GetService(NS_IOSERVICE_CONTRACTID);
   662     if (ioService)
   663     {
   664       ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   665       mIOService = ioService;
   666     }
   667   }
   669   bool isNoContentResponse = false;
   670   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   671   if (httpChannel) 
   672   {
   673     uint32_t response;
   674     isNoContentResponse = NS_SUCCEEDED(httpChannel->GetResponseStatus(&response)) &&
   675         (response == 204 || response == 205);
   676   }
   677   const bool isToplevelProgress = (windowForProgress.get() == window.get()) && !isNoContentResponse;
   679 #ifdef PR_LOGGING
   680   if (windowForProgress)
   681   {
   682     if (isToplevelProgress)
   683     {
   684       PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   685              ("SecureUI:%p: OnStateChange: progress: for toplevel\n", this));
   686     }
   687     else
   688     {
   689       PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   690              ("SecureUI:%p: OnStateChange: progress: for something else\n", this));
   691     }
   692   }
   693   else
   694   {
   695       PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   696              ("SecureUI:%p: OnStateChange: progress: no window known\n", this));
   697   }
   698 #endif
   700   PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   701          ("SecureUI:%p: OnStateChange\n", this));
   703   if (isViewSource)
   704     return NS_OK;
   706   if (!aRequest)
   707   {
   708     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   709            ("SecureUI:%p: OnStateChange with null request\n", this));
   710     return NS_ERROR_NULL_POINTER;
   711   }
   713 #ifdef PR_LOGGING
   714   if (PR_LOG_TEST(gSecureDocLog, PR_LOG_DEBUG)) {
   715     nsXPIDLCString reqname;
   716     aRequest->GetName(reqname);
   717     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   718            ("SecureUI:%p: %p %p OnStateChange %x %s\n", this, aWebProgress,
   719             aRequest, aProgressStateFlags, reqname.get()));
   720   }
   721 #endif
   723   nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
   725   nsCOMPtr<nsIURI> uri;
   726   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   727   if (channel) {
   728     channel->GetURI(getter_AddRefs(uri));
   729   }
   731   nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(aRequest));
   732   if (imgRequest) {
   733     NS_ASSERTION(!channel, "How did that happen, exactly?");
   734     // for image requests, we get the URI from here
   735     imgRequest->GetURI(getter_AddRefs(uri));
   736   }
   738   if (uri) {
   739     bool vs;
   740     if (NS_SUCCEEDED(uri->SchemeIs("javascript", &vs)) && vs) {
   741       // We ignore the progress events for javascript URLs.
   742       // If a document loading gets triggered, we will see more events.
   743       return NS_OK;
   744     }
   745   }
   747   uint32_t loadFlags = 0;
   748   aRequest->GetLoadFlags(&loadFlags);
   750 #ifdef PR_LOGGING
   751   if (aProgressStateFlags & STATE_START
   752       &&
   753       aProgressStateFlags & STATE_IS_REQUEST
   754       &&
   755       isToplevelProgress
   756       &&
   757       loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
   758   {
   759     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   760            ("SecureUI:%p: OnStateChange: SOMETHING STARTS FOR TOPMOST DOCUMENT\n", this));
   761   }
   763   if (aProgressStateFlags & STATE_STOP
   764       &&
   765       aProgressStateFlags & STATE_IS_REQUEST
   766       &&
   767       isToplevelProgress
   768       &&
   769       loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
   770   {
   771     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   772            ("SecureUI:%p: OnStateChange: SOMETHING STOPS FOR TOPMOST DOCUMENT\n", this));
   773   }
   774 #endif
   776   bool isSubDocumentRelevant = true;
   778   // We are only interested in requests that load in the browser window...
   779   if (!imgRequest) { // is not imgRequest
   780     nsCOMPtr<nsIHttpChannel> httpRequest(do_QueryInterface(aRequest));
   781     if (!httpRequest) {
   782       nsCOMPtr<nsIFileChannel> fileRequest(do_QueryInterface(aRequest));
   783       if (!fileRequest) {
   784         nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
   785         if (!wyciwygRequest) {
   786           nsCOMPtr<nsIFTPChannel> ftpRequest(do_QueryInterface(aRequest));
   787           if (!ftpRequest) {
   788             PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   789                    ("SecureUI:%p: OnStateChange: not relevant for sub content\n", this));
   790             isSubDocumentRelevant = false;
   791           }
   792         }
   793       }
   794     }
   795   }
   797   // This will ignore all resource, chrome, data, file, moz-icon, and anno
   798   // protocols. Local resources are treated as trusted.
   799   if (uri && ioService) {
   800     bool hasFlag;
   801     nsresult rv = 
   802       ioService->URIChainHasFlags(uri, 
   803                                   nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
   804                                   &hasFlag);
   805     if (NS_SUCCEEDED(rv) && hasFlag) {
   806       isSubDocumentRelevant = false;
   807     }
   808   }
   810 #if defined(DEBUG)
   811   nsCString info2;
   812   uint32_t testFlags = loadFlags;
   814   if (testFlags & nsIChannel::LOAD_DOCUMENT_URI)
   815   {
   816     testFlags -= nsIChannel::LOAD_DOCUMENT_URI;
   817     info2.Append("LOAD_DOCUMENT_URI ");
   818   }
   819   if (testFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
   820   {
   821     testFlags -= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
   822     info2.Append("LOAD_RETARGETED_DOCUMENT_URI ");
   823   }
   824   if (testFlags & nsIChannel::LOAD_REPLACE)
   825   {
   826     testFlags -= nsIChannel::LOAD_REPLACE;
   827     info2.Append("LOAD_REPLACE ");
   828   }
   830   const char *_status = NS_SUCCEEDED(aStatus) ? "1" : "0";
   832   nsCString info;
   833   uint32_t f = aProgressStateFlags;
   834   if (f & nsIWebProgressListener::STATE_START)
   835   {
   836     f -= nsIWebProgressListener::STATE_START;
   837     info.Append("START ");
   838   }
   839   if (f & nsIWebProgressListener::STATE_REDIRECTING)
   840   {
   841     f -= nsIWebProgressListener::STATE_REDIRECTING;
   842     info.Append("REDIRECTING ");
   843   }
   844   if (f & nsIWebProgressListener::STATE_TRANSFERRING)
   845   {
   846     f -= nsIWebProgressListener::STATE_TRANSFERRING;
   847     info.Append("TRANSFERRING ");
   848   }
   849   if (f & nsIWebProgressListener::STATE_NEGOTIATING)
   850   {
   851     f -= nsIWebProgressListener::STATE_NEGOTIATING;
   852     info.Append("NEGOTIATING ");
   853   }
   854   if (f & nsIWebProgressListener::STATE_STOP)
   855   {
   856     f -= nsIWebProgressListener::STATE_STOP;
   857     info.Append("STOP ");
   858   }
   859   if (f & nsIWebProgressListener::STATE_IS_REQUEST)
   860   {
   861     f -= nsIWebProgressListener::STATE_IS_REQUEST;
   862     info.Append("IS_REQUEST ");
   863   }
   864   if (f & nsIWebProgressListener::STATE_IS_DOCUMENT)
   865   {
   866     f -= nsIWebProgressListener::STATE_IS_DOCUMENT;
   867     info.Append("IS_DOCUMENT ");
   868   }
   869   if (f & nsIWebProgressListener::STATE_IS_NETWORK)
   870   {
   871     f -= nsIWebProgressListener::STATE_IS_NETWORK;
   872     info.Append("IS_NETWORK ");
   873   }
   874   if (f & nsIWebProgressListener::STATE_IS_WINDOW)
   875   {
   876     f -= nsIWebProgressListener::STATE_IS_WINDOW;
   877     info.Append("IS_WINDOW ");
   878   }
   879   if (f & nsIWebProgressListener::STATE_IS_INSECURE)
   880   {
   881     f -= nsIWebProgressListener::STATE_IS_INSECURE;
   882     info.Append("IS_INSECURE ");
   883   }
   884   if (f & nsIWebProgressListener::STATE_IS_BROKEN)
   885   {
   886     f -= nsIWebProgressListener::STATE_IS_BROKEN;
   887     info.Append("IS_BROKEN ");
   888   }
   889   if (f & nsIWebProgressListener::STATE_IS_SECURE)
   890   {
   891     f -= nsIWebProgressListener::STATE_IS_SECURE;
   892     info.Append("IS_SECURE ");
   893   }
   894   if (f & nsIWebProgressListener::STATE_SECURE_HIGH)
   895   {
   896     f -= nsIWebProgressListener::STATE_SECURE_HIGH;
   897     info.Append("SECURE_HIGH ");
   898   }
   899   if (f & nsIWebProgressListener::STATE_RESTORING)
   900   {
   901     f -= nsIWebProgressListener::STATE_RESTORING;
   902     info.Append("STATE_RESTORING ");
   903   }
   905   if (f > 0)
   906   {
   907     info.Append("f contains unknown flag!");
   908   }
   910   PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   911          ("SecureUI:%p: OnStateChange: %s %s -- %s\n", this, _status, 
   912           info.get(), info2.get()));
   914   if (aProgressStateFlags & STATE_STOP
   915       &&
   916       channel)
   917   {
   918     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
   919            ("SecureUI:%p: OnStateChange: seeing STOP with security state: %d\n", this,
   920             GetSecurityStateFromSecurityInfo(securityInfo)
   921             ));
   922   }
   923 #endif
   925   if (aProgressStateFlags & STATE_TRANSFERRING
   926       &&
   927       aProgressStateFlags & STATE_IS_REQUEST)
   928   {
   929     // The listing of a request in mTransferringRequests
   930     // means, there has already been data transfered.
   932     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   933     PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_ADD);
   935     return NS_OK;
   936   }
   938   bool requestHasTransferedData = false;
   940   if (aProgressStateFlags & STATE_STOP
   941       &&
   942       aProgressStateFlags & STATE_IS_REQUEST)
   943   {
   944     { /* scope for the ReentrantMonitorAutoEnter */
   945       ReentrantMonitorAutoEnter lock(mReentrantMonitor);
   946       PLDHashEntryHdr *entry = PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_LOOKUP);
   947       if (PL_DHASH_ENTRY_IS_BUSY(entry))
   948       {
   949         PL_DHashTableOperate(&mTransferringRequests, aRequest, PL_DHASH_REMOVE);
   951         requestHasTransferedData = true;
   952       }
   953     }
   955     if (!requestHasTransferedData) {
   956       // Because image loads doesn't support any TRANSFERRING notifications but
   957       // only START and STOP we must ask them directly whether content was
   958       // transferred.  See bug 432685 for details.
   959       nsCOMPtr<nsISecurityInfoProvider> securityInfoProvider =
   960         do_QueryInterface(aRequest);
   961       // Guess true in all failure cases to be safe.  But if we're not
   962       // an nsISecurityInfoProvider, then we just haven't transferred
   963       // any data.
   964       bool hasTransferred;
   965       requestHasTransferedData =
   966         securityInfoProvider &&
   967         (NS_FAILED(securityInfoProvider->GetHasTransferredData(&hasTransferred)) ||
   968          hasTransferred);
   969     }
   970   }
   972   bool allowSecurityStateChange = true;
   973   if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
   974   {
   975     // The original consumer (this) is no longer the target of the load.
   976     // Ignore any events with this flag, do not allow them to update
   977     // our secure UI state.
   978     allowSecurityStateChange = false;
   979   }
   981   if (aProgressStateFlags & STATE_START
   982       &&
   983       aProgressStateFlags & STATE_IS_REQUEST
   984       &&
   985       isToplevelProgress
   986       &&
   987       loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
   988   {
   989     bool inProgress;
   991     int32_t saveSubBroken;
   992     int32_t saveSubNo;
   993     nsCOMPtr<nsIAssociatedContentSecurity> prevContentSecurity;
   995     int32_t newSubBroken = 0;
   996     int32_t newSubNo = 0;
   998     {
   999       ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1000       inProgress = (mDocumentRequestsInProgress!=0);
  1002       if (allowSecurityStateChange && !inProgress)
  1004         saveSubBroken = mSubRequestsBrokenSecurity;
  1005         saveSubNo = mSubRequestsNoSecurity;
  1006         prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
  1010     if (allowSecurityStateChange && !inProgress)
  1012       PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1013              ("SecureUI:%p: OnStateChange: start for toplevel document\n", this
  1014               ));
  1016       if (prevContentSecurity)
  1018         PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1019                ("SecureUI:%p: OnStateChange: start, saving current sub state\n", this
  1020                 ));
  1022         // before resetting our state, let's save information about
  1023         // sub element loads, so we can restore it later
  1024         prevContentSecurity->SetCountSubRequestsBrokenSecurity(saveSubBroken);
  1025         prevContentSecurity->SetCountSubRequestsNoSecurity(saveSubNo);
  1026         prevContentSecurity->Flush();
  1027         PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI:%p: Saving subs in START to %p as %d,%d\n", 
  1028           this, prevContentSecurity.get(), saveSubBroken, saveSubNo));      
  1031       bool retrieveAssociatedState = false;
  1033       if (securityInfo &&
  1034           (aProgressStateFlags & nsIWebProgressListener::STATE_RESTORING) != 0) {
  1035         retrieveAssociatedState = true;
  1036       } else {
  1037         nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
  1038         if (wyciwygRequest) {
  1039           retrieveAssociatedState = true;
  1043       if (retrieveAssociatedState)
  1045         // When restoring from bfcache, we will not get events for the 
  1046         // page's sub elements, so let's load the state of sub elements
  1047         // from the cache.
  1049         nsCOMPtr<nsIAssociatedContentSecurity> 
  1050           newContentSecurity(do_QueryInterface(securityInfo));
  1052         if (newContentSecurity)
  1054           PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1055                  ("SecureUI:%p: OnStateChange: start, loading old sub state\n", this
  1056                   ));
  1058           newContentSecurity->GetCountSubRequestsBrokenSecurity(&newSubBroken);
  1059           newContentSecurity->GetCountSubRequestsNoSecurity(&newSubNo);
  1060           PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI:%p: Restoring subs in START from %p to %d,%d\n", 
  1061             this, newContentSecurity.get(), newSubBroken, newSubNo));      
  1064       else
  1066         // If we don't get OnLocationChange for this top level load later,
  1067         // it didn't get rendered.  But we reset the state to unknown and
  1068         // mSubRequests* to zeros.  If we would have left these values after 
  1069         // this top level load stoped, we would override the original top level
  1070         // load with all zeros and break mixed content state on back and forward.
  1071         mRestoreSubrequests = true;
  1076       ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1078       if (allowSecurityStateChange && !inProgress)
  1080         ResetStateTracking();
  1081         mSubRequestsBrokenSecurity = newSubBroken;
  1082         mSubRequestsNoSecurity = newSubNo;
  1083         mNewToplevelSecurityStateKnown = false;
  1086       // By using a counter, this code also works when the toplevel
  1087       // document get's redirected, but the STOP request for the 
  1088       // previous toplevel document has not yet have been received.
  1089       PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1090              ("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this
  1091               ));
  1092       ++mDocumentRequestsInProgress;
  1095     return NS_OK;
  1098   if (aProgressStateFlags & STATE_STOP
  1099       &&
  1100       aProgressStateFlags & STATE_IS_REQUEST
  1101       &&
  1102       isToplevelProgress
  1103       &&
  1104       loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
  1106     int32_t temp_DocumentRequestsInProgress;
  1107     nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
  1110       ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1111       temp_DocumentRequestsInProgress = mDocumentRequestsInProgress;
  1112       if (allowSecurityStateChange)
  1114         temp_ToplevelEventSink = mToplevelEventSink;
  1118     if (temp_DocumentRequestsInProgress <= 0)
  1120       // Ignore stop requests unless a document load is in progress
  1121       // Unfortunately on application start, see some stops without having seen any starts...
  1122       return NS_OK;
  1125     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1126            ("SecureUI:%p: OnStateChange: --mDocumentRequestsInProgress\n", this
  1127             ));
  1129     if (!temp_ToplevelEventSink && channel)
  1131       if (allowSecurityStateChange)
  1133         ObtainEventSink(channel, temp_ToplevelEventSink);
  1137     bool sinkChanged = false;
  1138     bool inProgress;
  1140       ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1141       if (allowSecurityStateChange)
  1143         sinkChanged = (mToplevelEventSink != temp_ToplevelEventSink);
  1144         mToplevelEventSink = temp_ToplevelEventSink;
  1146       --mDocumentRequestsInProgress;
  1147       inProgress = mDocumentRequestsInProgress > 0;
  1150     if (allowSecurityStateChange && requestHasTransferedData) {
  1151       // Data has been transferred for the single toplevel
  1152       // request. Evaluate the security state.
  1154       // Do this only when the sink has changed.  We update and notify
  1155       // the state from OnLacationChange, this is actually redundant.
  1156       // But when the target sink changes between OnLocationChange and
  1157       // OnStateChange, we have to fire the notification here (again).
  1159       if (sinkChanged || mOnLocationChangeSeen)
  1160         return EvaluateAndUpdateSecurityState(aRequest, securityInfo, false);
  1162     mOnLocationChangeSeen = false;
  1164     if (mRestoreSubrequests && !inProgress)
  1166       // We get here when there were no OnLocationChange between 
  1167       // OnStateChange(START) and OnStateChange(STOP).  Then the load has not
  1168       // been rendered but has been retargeted in some other way then by external
  1169       // app handler.  Restore mSubRequests* members to what the current security 
  1170       // state info holds (it was reset to all zero in OnStateChange(START) 
  1171       // before).
  1172       nsCOMPtr<nsIAssociatedContentSecurity> currentContentSecurity;
  1174         ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1175         currentContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
  1177         // Drop this indication flag, the restore opration is just being
  1178         // done.
  1179         mRestoreSubrequests = false;
  1181         // We can do this since the state didn't actually change.
  1182         mNewToplevelSecurityStateKnown = true;
  1185       int32_t subBroken = 0;
  1186       int32_t subNo = 0;
  1188       if (currentContentSecurity)
  1190         currentContentSecurity->GetCountSubRequestsBrokenSecurity(&subBroken);
  1191         currentContentSecurity->GetCountSubRequestsNoSecurity(&subNo);
  1192         PR_LOG(gSecureDocLog, PR_LOG_DEBUG, ("SecureUI:%p: Restoring subs in STOP from %p to %d,%d\n", 
  1193           this, currentContentSecurity.get(), subBroken, subNo));      
  1197         ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1198         mSubRequestsBrokenSecurity = subBroken;
  1199         mSubRequestsNoSecurity = subNo;
  1203     return NS_OK;
  1206   if (aProgressStateFlags & STATE_STOP
  1207       &&
  1208       aProgressStateFlags & STATE_IS_REQUEST)
  1210     if (!isSubDocumentRelevant)
  1211       return NS_OK;
  1213     // if we arrive here, LOAD_DOCUMENT_URI is not set
  1215     // We only care for the security state of sub requests which have actually transfered data.
  1217     if (allowSecurityStateChange && requestHasTransferedData)
  1219       UpdateSubrequestMembers(securityInfo);
  1221       // Care for the following scenario:
  1222       // A new top level document load might have already started,
  1223       // but the security state of the new top level document might not yet been known.
  1224       // 
  1225       // At this point, we are learning about the security state of a sub-document.
  1226       // We must not update the security state based on the sub content,
  1227       // if the new top level state is not yet known.
  1228       //
  1229       // We skip updating the security state in this case.
  1231       bool temp_NewToplevelSecurityStateKnown;
  1233         ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1234         temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown;
  1237       if (temp_NewToplevelSecurityStateKnown)
  1238         return UpdateSecurityState(aRequest, false, false);
  1241     return NS_OK;
  1244   return NS_OK;
  1247 // I'm keeping this as a separate function, in order to simplify the review
  1248 // for bug 412456. We should inline this in a follow up patch.
  1249 void nsSecureBrowserUIImpl::ObtainEventSink(nsIChannel *channel, 
  1250                                             nsCOMPtr<nsISecurityEventSink> &sink)
  1252   if (!sink)
  1253     NS_QueryNotificationCallbacks(channel, sink);
  1256 nsresult nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest, 
  1257                                                     bool withNewLocation, 
  1258                                                     bool withUpdateStatus)
  1260   lockIconState warnSecurityState = lis_no_security;
  1261   nsresult rv = NS_OK;
  1263   // both parameters are both input and outout
  1264   bool flagsChanged = UpdateMyFlags(warnSecurityState);
  1266   if (flagsChanged || withNewLocation || withUpdateStatus)
  1267     rv = TellTheWorld(warnSecurityState, aRequest);
  1269   return rv;
  1272 // must not fail, by definition, only trivial assignments
  1273 // or string operations are allowed
  1274 // returns true if our overall state has changed and we must send out notifications
  1275 bool nsSecureBrowserUIImpl::UpdateMyFlags(lockIconState &warnSecurityState)
  1277   ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1278   bool mustTellTheWorld = false;
  1280   lockIconState newSecurityState;
  1282   if (mNewToplevelSecurityState & STATE_IS_SECURE)
  1284     if (mSubRequestsBrokenSecurity
  1285         ||
  1286         mSubRequestsNoSecurity)
  1288       newSecurityState = lis_mixed_security;
  1290     else
  1292       newSecurityState = lis_high_security;
  1295   else
  1296   if (mNewToplevelSecurityState & STATE_IS_BROKEN)
  1298     // indicating BROKEN is more important than MIXED.
  1300     newSecurityState = lis_broken_security;
  1302   else
  1304     newSecurityState = lis_no_security;
  1307   PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1308          ("SecureUI:%p: UpdateSecurityState:  old-new  %d - %d\n", this,
  1309          mNotifiedSecurityState, newSecurityState
  1310           ));
  1312   if (mNotifiedSecurityState != newSecurityState)
  1314     mustTellTheWorld = true;
  1316     // we'll treat "broken" exactly like "insecure",
  1318     /*
  1319       security    icon
  1320       ----------------
  1322       no          open
  1323       mixed       broken
  1324       broken      broken
  1325       high        high
  1326     */
  1328     mNotifiedSecurityState = newSecurityState;
  1330     if (lis_no_security == newSecurityState)
  1332       mSSLStatus = nullptr;
  1336   if (mNotifiedToplevelIsEV != mNewToplevelIsEV) {
  1337     mustTellTheWorld = true;
  1338     mNotifiedToplevelIsEV = mNewToplevelIsEV;
  1341   return mustTellTheWorld;
  1344 nsresult nsSecureBrowserUIImpl::TellTheWorld(lockIconState warnSecurityState, 
  1345                                              nsIRequest* aRequest)
  1347   nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
  1348   lockIconState temp_NotifiedSecurityState;
  1349   bool temp_NotifiedToplevelIsEV;
  1352     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1353     temp_ToplevelEventSink = mToplevelEventSink;
  1354     temp_NotifiedSecurityState = mNotifiedSecurityState;
  1355     temp_NotifiedToplevelIsEV = mNotifiedToplevelIsEV;
  1358   if (temp_ToplevelEventSink)
  1360     uint32_t newState = STATE_IS_INSECURE;
  1361     MapInternalToExternalState(&newState, 
  1362                                temp_NotifiedSecurityState, 
  1363                                temp_NotifiedToplevelIsEV);
  1365     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1366            ("SecureUI:%p: UpdateSecurityState: calling OnSecurityChange\n", this
  1367             ));
  1369     temp_ToplevelEventSink->OnSecurityChange(aRequest, newState);
  1371   else
  1373     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1374            ("SecureUI:%p: UpdateSecurityState: NO mToplevelEventSink!\n", this
  1375             ));
  1379   return NS_OK; 
  1382 NS_IMETHODIMP
  1383 nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
  1384                                         nsIRequest* aRequest,
  1385                                         nsIURI* aLocation,
  1386                                         uint32_t aFlags)
  1388 #ifdef DEBUG
  1389   nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
  1390   NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
  1391                "unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
  1392 #endif
  1393   PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1394          ("SecureUI:%p: OnLocationChange\n", this));
  1396   bool updateIsViewSource = false;
  1397   bool temp_IsViewSource = false;
  1398   nsCOMPtr<nsIDOMWindow> window;
  1400   if (aLocation)
  1402     bool vs;
  1404     nsresult rv = aLocation->SchemeIs("view-source", &vs);
  1405     NS_ENSURE_SUCCESS(rv, rv);
  1407     if (vs) {
  1408       PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1409              ("SecureUI:%p: OnLocationChange: view-source\n", this));
  1412     updateIsViewSource = true;
  1413     temp_IsViewSource = vs;
  1417     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1418     if (updateIsViewSource) {
  1419       mIsViewSource = temp_IsViewSource;
  1421     mCurrentURI = aLocation;
  1422     window = do_QueryReferent(mWindow);
  1423     NS_ASSERTION(window, "Window has gone away?!");
  1426   // When |aRequest| is null, basically we don't trust that document. But if
  1427   // docshell insists that the document has not changed at all, we will reuse
  1428   // the previous security state, no matter what |aRequest| may be.
  1429   if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT)
  1430     return NS_OK;
  1432   // The location bar has changed, so we must update the security state.  The
  1433   // only concern with doing this here is that a page may transition from being
  1434   // reported as completely secure to being reported as partially secure
  1435   // (mixed).  This may be confusing for users, and it may bother users who
  1436   // like seeing security dialogs.  However, it seems prudent given that page
  1437   // loading may never end in some edge cases (perhaps by a site with malicious
  1438   // intent).
  1440   nsCOMPtr<nsIDOMWindow> windowForProgress;
  1441   aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
  1443   nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
  1445   if (windowForProgress.get() == window.get()) {
  1446     // For toplevel channels, update the security state right away.
  1447     mOnLocationChangeSeen = true;
  1448     return EvaluateAndUpdateSecurityState(aRequest, securityInfo, true);
  1451   // For channels in subdocuments we only update our subrequest state members.
  1452   UpdateSubrequestMembers(securityInfo);
  1454   // Care for the following scenario:
  1456   // A new toplevel document load might have already started, but the security
  1457   // state of the new toplevel document might not yet be known.
  1458   // 
  1459   // At this point, we are learning about the security state of a sub-document.
  1460   // We must not update the security state based on the sub content, if the new
  1461   // top level state is not yet known.
  1462   //
  1463   // We skip updating the security state in this case.
  1465   bool temp_NewToplevelSecurityStateKnown;
  1467     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1468     temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown;
  1471   if (temp_NewToplevelSecurityStateKnown)
  1472     return UpdateSecurityState(aRequest, true, false);
  1474   return NS_OK;
  1477 NS_IMETHODIMP
  1478 nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress* aWebProgress,
  1479                                       nsIRequest* aRequest,
  1480                                       nsresult aStatus,
  1481                                       const char16_t* aMessage)
  1483   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
  1484   return NS_OK;
  1487 nsresult
  1488 nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress *aWebProgress,
  1489                                         nsIRequest *aRequest,
  1490                                         uint32_t state)
  1492 #if defined(DEBUG)
  1493   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  1494   if (!channel)
  1495     return NS_OK;
  1497   nsCOMPtr<nsIURI> aURI;
  1498   channel->GetURI(getter_AddRefs(aURI));
  1500   if (aURI) {
  1501     nsAutoCString temp;
  1502     aURI->GetSpec(temp);
  1503     PR_LOG(gSecureDocLog, PR_LOG_DEBUG,
  1504            ("SecureUI:%p: OnSecurityChange: (%x) %s\n", this,
  1505             state, temp.get()));
  1507 #endif
  1509   return NS_OK;
  1512 // nsISSLStatusProvider methods
  1513 NS_IMETHODIMP
  1514 nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result)
  1516   NS_ENSURE_ARG_POINTER(_result);
  1518   ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1520   switch (mNotifiedSecurityState)
  1522     case lis_mixed_security:
  1523     case lis_high_security:
  1524       break;
  1526     default:
  1527       NS_NOTREACHED("if this is reached you must add more entries to the switch");
  1528     case lis_no_security:
  1529     case lis_broken_security:
  1530       *_result = nullptr;
  1531       return NS_OK;
  1534   *_result = mSSLStatus;
  1535   NS_IF_ADDREF(*_result);
  1537   return NS_OK;
  1540 nsresult
  1541 nsSecureBrowserUIImpl::IsURLHTTPS(nsIURI* aURL, bool* value)
  1543   *value = false;
  1545   if (!aURL)
  1546     return NS_OK;
  1548   return aURL->SchemeIs("https", value);
  1551 nsresult
  1552 nsSecureBrowserUIImpl::IsURLJavaScript(nsIURI* aURL, bool* value)
  1554   *value = false;
  1556   if (!aURL)
  1557     return NS_OK;
  1559   return aURL->SchemeIs("javascript", value);
  1562 nsresult
  1563 nsSecureBrowserUIImpl::CheckPost(nsIURI *formURL, nsIURI *actionURL, bool *okayToPost)
  1565   bool formSecure, actionSecure, actionJavaScript;
  1566   *okayToPost = true;
  1568   nsresult rv = IsURLHTTPS(formURL, &formSecure);
  1569   if (NS_FAILED(rv))
  1570     return rv;
  1572   rv = IsURLHTTPS(actionURL, &actionSecure);
  1573   if (NS_FAILED(rv))
  1574     return rv;
  1576   rv = IsURLJavaScript(actionURL, &actionJavaScript);
  1577   if (NS_FAILED(rv))
  1578     return rv;
  1580   // If we are posting to a secure link, all is okay.
  1581   // It doesn't matter whether the currently viewed page is secure or not,
  1582   // because the data will be sent to a secure URL.
  1583   if (actionSecure) {
  1584     return NS_OK;
  1587   // Action is a JavaScript call, not an actual post. That's okay too.
  1588   if (actionJavaScript) {
  1589     return NS_OK;
  1592   // posting to insecure webpage from a secure webpage.
  1593   if (formSecure) {
  1594     *okayToPost = ConfirmPostToInsecureFromSecure();
  1597   return NS_OK;
  1600 //
  1601 // Implementation of an nsIInterfaceRequestor for use
  1602 // as context for NSS calls
  1603 //
  1604 class nsUIContext : public nsIInterfaceRequestor
  1606 public:
  1607   NS_DECL_ISUPPORTS
  1608   NS_DECL_NSIINTERFACEREQUESTOR
  1610   nsUIContext(nsIDOMWindow *window);
  1611   virtual ~nsUIContext();
  1613 private:
  1614   nsCOMPtr<nsIDOMWindow> mWindow;
  1615 };
  1617 NS_IMPL_ISUPPORTS(nsUIContext, nsIInterfaceRequestor)
  1619 nsUIContext::nsUIContext(nsIDOMWindow *aWindow)
  1620 : mWindow(aWindow)
  1624 nsUIContext::~nsUIContext()
  1628 /* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
  1629 NS_IMETHODIMP nsUIContext::GetInterface(const nsIID & uuid, void * *result)
  1631   NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
  1632   nsresult rv;
  1634   if (uuid.Equals(NS_GET_IID(nsIPrompt))) {
  1635     nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mWindow, &rv);
  1636     if (NS_FAILED(rv)) return rv;
  1638     nsIPrompt *prompt;
  1640     rv = window->GetPrompter(&prompt);
  1641     *result = prompt;
  1642   } else if (uuid.Equals(NS_GET_IID(nsIDOMWindow))) {
  1643     *result = mWindow;
  1644     NS_ADDREF ((nsISupports*) *result);
  1645     rv = NS_OK;
  1646   } else {
  1647     rv = NS_ERROR_NO_INTERFACE;
  1650   return rv;
  1653 bool
  1654 nsSecureBrowserUIImpl::GetNSSDialogs(nsCOMPtr<nsISecurityWarningDialogs> & dialogs,
  1655                                      nsCOMPtr<nsIInterfaceRequestor> & ctx)
  1657   if (!NS_IsMainThread()) {
  1658     NS_ERROR("nsSecureBrowserUIImpl::GetNSSDialogs called off the main thread");
  1659     return false;
  1662   dialogs = do_GetService(NS_SECURITYWARNINGDIALOGS_CONTRACTID);
  1663   if (!dialogs)
  1664     return false;
  1666   nsCOMPtr<nsIDOMWindow> window;
  1668     ReentrantMonitorAutoEnter lock(mReentrantMonitor);
  1669     window = do_QueryReferent(mWindow);
  1670     NS_ASSERTION(window, "Window has gone away?!");
  1672   ctx = new nsUIContext(window);
  1674   return true;
  1677 /**
  1678  * ConfirmPostToInsecureFromSecure - returns true if
  1679  *   the user approves the submit (or doesn't care).
  1680  *   returns false on errors.
  1681  */
  1682 bool nsSecureBrowserUIImpl::
  1683 ConfirmPostToInsecureFromSecure()
  1685   nsCOMPtr<nsISecurityWarningDialogs> dialogs;
  1686   nsCOMPtr<nsIInterfaceRequestor> ctx;
  1688   if (!GetNSSDialogs(dialogs, ctx)) {
  1689     return false; // Should this allow true for unimplemented?
  1692   bool result;
  1694   nsresult rv = dialogs->ConfirmPostToInsecureFromSecure(ctx, &result);
  1695   if (NS_FAILED(rv)) return false;
  1697   return result;

mercurial