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