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

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

mercurial