Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsSecureBrowserUIImpl.h"
7 :
8 : #include "imgIRequest.h"
9 : #include "mozilla/Assertions.h"
10 : #include "mozilla/IntegerPrintfMacros.h"
11 : #include "mozilla/Logging.h"
12 : #include "nsCURILoader.h"
13 : #include "nsIAssociatedContentSecurity.h"
14 : #include "nsIChannel.h"
15 : #include "nsIDOMWindow.h"
16 : #include "nsIDocShell.h"
17 : #include "nsIDocShellTreeItem.h"
18 : #include "nsIDocument.h"
19 : #include "nsIFTPChannel.h"
20 : #include "nsIFileChannel.h"
21 : #include "nsIHttpChannel.h"
22 : #include "nsIInterfaceRequestorUtils.h"
23 : #include "nsIProtocolHandler.h"
24 : #include "nsISSLStatus.h"
25 : #include "nsISecurityInfoProvider.h"
26 : #include "nsIServiceManager.h"
27 : #include "nsITransportSecurityInfo.h"
28 : #include "nsIWebProgress.h"
29 : #include "nsIWyciwygChannel.h"
30 : #include "nsNetCID.h"
31 : #include "nsNetUtil.h"
32 : #include "nsPIDOMWindow.h"
33 : #include "nsThreadUtils.h"
34 : #include "nspr.h"
35 : #include "nsString.h"
36 :
37 : using namespace mozilla;
38 :
39 : LazyLogModule gSecureDocLog("nsSecureBrowserUI");
40 :
41 : struct RequestHashEntry : PLDHashEntryHdr {
42 : void *r;
43 : };
44 :
45 : static bool
46 5 : RequestMapMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
47 : {
48 5 : const RequestHashEntry *entry = static_cast<const RequestHashEntry*>(hdr);
49 5 : return entry->r == key;
50 : }
51 :
52 : static void
53 5 : RequestMapInitEntry(PLDHashEntryHdr *hdr, const void *key)
54 : {
55 5 : RequestHashEntry *entry = static_cast<RequestHashEntry*>(hdr);
56 5 : entry->r = (void*)key;
57 5 : }
58 :
59 : static const PLDHashTableOps gMapOps = {
60 : PLDHashTable::HashVoidPtrKeyStub,
61 : RequestMapMatchEntry,
62 : PLDHashTable::MoveEntryStub,
63 : PLDHashTable::ClearEntryStub,
64 : RequestMapInitEntry
65 : };
66 :
67 2 : nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
68 : : mNotifiedSecurityState(lis_no_security)
69 : , mNotifiedToplevelIsEV(false)
70 : , mNewToplevelSecurityState(STATE_IS_INSECURE)
71 : , mNewToplevelIsEV(false)
72 : , mNewToplevelSecurityStateKnown(true)
73 : , mIsViewSource(false)
74 : , mSubRequestsBrokenSecurity(0)
75 : , mSubRequestsNoSecurity(0)
76 : , mCertUserOverridden(false)
77 : , mRestoreSubrequests(false)
78 : , mOnLocationChangeSeen(false)
79 : #ifdef DEBUG
80 : , mEntered(false)
81 : #endif
82 2 : , mTransferringRequests(&gMapOps, sizeof(RequestHashEntry))
83 : {
84 2 : MOZ_ASSERT(NS_IsMainThread());
85 :
86 2 : ResetStateTracking();
87 2 : }
88 :
89 301 : NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
90 : nsISecureBrowserUI,
91 : nsIWebProgressListener,
92 : nsISupportsWeakReference,
93 : nsISSLStatusProvider)
94 :
95 : NS_IMETHODIMP
96 2 : nsSecureBrowserUIImpl::Init(mozIDOMWindowProxy* aWindow)
97 : {
98 2 : MOZ_ASSERT(NS_IsMainThread());
99 :
100 2 : if (MOZ_LOG_TEST(gSecureDocLog, LogLevel::Debug)) {
101 0 : nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
102 :
103 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
104 : ("SecureUI:%p: Init: mWindow: %p, aWindow: %p\n", this,
105 : window.get(), aWindow));
106 : }
107 :
108 2 : if (!aWindow) {
109 0 : NS_WARNING("Null window passed to nsSecureBrowserUIImpl::Init()");
110 0 : return NS_ERROR_INVALID_ARG;
111 : }
112 :
113 2 : if (mWindow) {
114 0 : NS_WARNING("Trying to init an nsSecureBrowserUIImpl twice");
115 0 : return NS_ERROR_ALREADY_INITIALIZED;
116 : }
117 :
118 : nsresult rv;
119 2 : mWindow = do_GetWeakReference(aWindow, &rv);
120 2 : NS_ENSURE_SUCCESS(rv, rv);
121 :
122 2 : auto* piwindow = nsPIDOMWindowOuter::From(aWindow);
123 2 : nsIDocShell *docShell = piwindow->GetDocShell();
124 :
125 : // The Docshell will own the SecureBrowserUI object
126 2 : if (!docShell)
127 0 : return NS_ERROR_FAILURE;
128 :
129 2 : docShell->SetSecurityUI(this);
130 :
131 : /* GetWebProgress(mWindow) */
132 : // hook up to the webprogress notifications.
133 4 : nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
134 2 : if (!wp) return NS_ERROR_FAILURE;
135 : /* end GetWebProgress */
136 :
137 4 : wp->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
138 : nsIWebProgress::NOTIFY_STATE_ALL |
139 : nsIWebProgress::NOTIFY_LOCATION |
140 4 : nsIWebProgress::NOTIFY_SECURITY);
141 :
142 :
143 2 : return NS_OK;
144 : }
145 :
146 : NS_IMETHODIMP
147 2 : nsSecureBrowserUIImpl::GetState(uint32_t* aState)
148 : {
149 2 : MOZ_ASSERT(NS_IsMainThread());
150 2 : return MapInternalToExternalState(aState, mNotifiedSecurityState,
151 4 : mNotifiedToplevelIsEV);
152 : }
153 :
154 : // static
155 : already_AddRefed<nsISupports>
156 33 : nsSecureBrowserUIImpl::ExtractSecurityInfo(nsIRequest* aRequest)
157 : {
158 66 : nsCOMPtr<nsISupports> retval;
159 66 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
160 33 : if (channel)
161 26 : channel->GetSecurityInfo(getter_AddRefs(retval));
162 :
163 33 : if (!retval) {
164 66 : nsCOMPtr<nsISecurityInfoProvider> provider(do_QueryInterface(aRequest));
165 33 : if (provider)
166 0 : provider->GetSecurityInfo(getter_AddRefs(retval));
167 : }
168 :
169 66 : return retval.forget();
170 : }
171 :
172 : nsresult
173 2 : nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconState lock, bool ev)
174 : {
175 2 : NS_ENSURE_ARG(aState);
176 :
177 2 : switch (lock)
178 : {
179 : case lis_broken_security:
180 0 : *aState = STATE_IS_BROKEN;
181 0 : break;
182 :
183 : case lis_mixed_security:
184 0 : *aState = STATE_IS_BROKEN;
185 0 : break;
186 :
187 : case lis_high_security:
188 0 : *aState = STATE_IS_SECURE | STATE_SECURE_HIGH;
189 0 : break;
190 :
191 : default:
192 : case lis_no_security:
193 2 : *aState = STATE_IS_INSECURE;
194 2 : break;
195 : }
196 :
197 2 : if (ev && (*aState & STATE_IS_SECURE))
198 0 : *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
199 :
200 2 : if (mCertUserOverridden && (*aState & STATE_IS_SECURE)) {
201 0 : *aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
202 : }
203 :
204 4 : nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
205 2 : if (!docShell)
206 0 : return NS_OK;
207 :
208 : // For content docShell's, the mixed content security state is set on the root docShell.
209 2 : if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
210 4 : nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(docShell));
211 4 : nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
212 2 : docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
213 2 : MOZ_ASSERT(sameTypeRoot,
214 : "No document shell root tree item from document shell tree item!");
215 2 : docShell = do_QueryInterface(sameTypeRoot);
216 2 : if (!docShell)
217 0 : return NS_OK;
218 : }
219 :
220 : // Has a Mixed Content Load initiated in nsMixedContentBlocker?
221 : // * If not, the state should not be broken because of mixed content;
222 : // overriding the previous state if it is inaccurately flagged as mixed.
223 2 : if (lock == lis_mixed_security &&
224 0 : !docShell->GetHasMixedActiveContentLoaded() &&
225 0 : !docShell->GetHasMixedDisplayContentLoaded() &&
226 2 : !docShell->GetHasMixedActiveContentBlocked() &&
227 0 : !docShell->GetHasMixedDisplayContentBlocked()) {
228 0 : *aState = STATE_IS_SECURE;
229 0 : if (ev) {
230 0 : *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
231 : }
232 : }
233 : // * If so, the state should be broken or insecure; overriding the previous
234 : // state set by the lock parameter.
235 2 : uint32_t tempState = STATE_IS_BROKEN;
236 2 : if (lock == lis_no_security) {
237 : // this is to ensure that http: pages with mixed content in nested
238 : // iframes don't get marked as broken instead of insecure
239 2 : tempState = STATE_IS_INSECURE;
240 : }
241 2 : if (docShell->GetHasMixedActiveContentLoaded() &&
242 0 : docShell->GetHasMixedDisplayContentLoaded()) {
243 0 : *aState = tempState |
244 0 : nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
245 : nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
246 2 : } else if (docShell->GetHasMixedActiveContentLoaded()) {
247 0 : *aState = tempState |
248 : nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
249 2 : } else if (docShell->GetHasMixedDisplayContentLoaded()) {
250 0 : *aState = tempState |
251 : nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
252 : }
253 :
254 2 : if (mCertUserOverridden) {
255 0 : *aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
256 : }
257 :
258 : // Has Mixed Content Been Blocked in nsMixedContentBlocker?
259 2 : if (docShell->GetHasMixedActiveContentBlocked())
260 0 : *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
261 :
262 2 : if (docShell->GetHasMixedDisplayContentBlocked())
263 0 : *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
264 :
265 : // Has Tracking Content been Blocked?
266 2 : if (docShell->GetHasTrackingContentBlocked())
267 0 : *aState |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
268 :
269 2 : if (docShell->GetHasTrackingContentLoaded())
270 0 : *aState |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
271 :
272 2 : return NS_OK;
273 : }
274 :
275 : NS_IMETHODIMP
276 2 : nsSecureBrowserUIImpl::SetDocShell(nsIDocShell* aDocShell)
277 : {
278 2 : MOZ_ASSERT(NS_IsMainThread());
279 : nsresult rv;
280 2 : mDocShell = do_GetWeakReference(aDocShell, &rv);
281 2 : return rv;
282 : }
283 :
284 5 : static uint32_t GetSecurityStateFromSecurityInfoAndRequest(nsISupports* info,
285 : nsIRequest* request)
286 : {
287 : nsresult res;
288 : uint32_t securityState;
289 :
290 10 : nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
291 5 : if (!psmInfo) {
292 5 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - no nsITransportSecurityInfo for %p\n",
293 : (nsISupports *)info));
294 5 : return nsIWebProgressListener::STATE_IS_INSECURE;
295 : }
296 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - info is %p\n",
297 : (nsISupports *)info));
298 :
299 0 : res = psmInfo->GetSecurityState(&securityState);
300 0 : if (NS_FAILED(res)) {
301 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - GetSecurityState failed: %" PRIu32 "\n",
302 : static_cast<uint32_t>(res)));
303 0 : securityState = nsIWebProgressListener::STATE_IS_BROKEN;
304 : }
305 :
306 0 : if (securityState != nsIWebProgressListener::STATE_IS_INSECURE) {
307 : // A secure connection does not yield a secure per-uri channel if the
308 : // scheme is plain http.
309 :
310 0 : nsCOMPtr<nsIURI> uri;
311 0 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
312 0 : if (channel) {
313 0 : channel->GetURI(getter_AddRefs(uri));
314 : } else {
315 0 : nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(request));
316 0 : if (imgRequest) {
317 0 : imgRequest->GetURI(getter_AddRefs(uri));
318 : }
319 : }
320 0 : if (uri) {
321 : bool isHttp, isFtp;
322 0 : if ((NS_SUCCEEDED(uri->SchemeIs("http", &isHttp)) && isHttp) ||
323 0 : (NS_SUCCEEDED(uri->SchemeIs("ftp", &isFtp)) && isFtp)) {
324 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - "
325 : "channel scheme is insecure.\n"));
326 0 : securityState = nsIWebProgressListener::STATE_IS_INSECURE;
327 : }
328 : }
329 : }
330 :
331 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - Returning %d\n",
332 : securityState));
333 0 : return securityState;
334 : }
335 :
336 :
337 : // nsIWebProgressListener
338 : NS_IMETHODIMP
339 0 : nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress*, nsIRequest*, int32_t,
340 : int32_t, int32_t, int32_t)
341 : {
342 0 : MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
343 : return NS_OK;
344 : }
345 :
346 : void
347 5 : nsSecureBrowserUIImpl::ResetStateTracking()
348 : {
349 5 : mDocumentRequestsInProgress = 0;
350 5 : mTransferringRequests.Clear();
351 5 : }
352 :
353 : void
354 3 : nsSecureBrowserUIImpl::EvaluateAndUpdateSecurityState(nsIRequest* aRequest,
355 : nsISupports* info,
356 : bool withNewLocation,
357 : bool withNewSink)
358 : {
359 3 : mNewToplevelIsEV = false;
360 :
361 3 : bool updateStatus = false;
362 6 : nsCOMPtr<nsISSLStatus> temp_SSLStatus;
363 :
364 3 : mNewToplevelSecurityState =
365 3 : GetSecurityStateFromSecurityInfoAndRequest(info, aRequest);
366 :
367 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
368 : ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n",
369 : this, mNewToplevelSecurityState));
370 :
371 6 : nsCOMPtr<nsISSLStatusProvider> sp(do_QueryInterface(info));
372 3 : if (sp) {
373 : // Ignore result
374 0 : updateStatus = true;
375 0 : (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
376 0 : if (temp_SSLStatus) {
377 : bool aTemp;
378 0 : if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) {
379 0 : mNewToplevelIsEV = aTemp;
380 : }
381 : }
382 : }
383 :
384 3 : mNewToplevelSecurityStateKnown = true;
385 3 : if (updateStatus) {
386 0 : mSSLStatus = temp_SSLStatus;
387 : }
388 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
389 : ("SecureUI:%p: remember securityInfo %p\n", this,
390 : info));
391 : nsCOMPtr<nsIAssociatedContentSecurity> associatedContentSecurityFromRequest(
392 6 : do_QueryInterface(aRequest));
393 3 : if (associatedContentSecurityFromRequest) {
394 0 : mCurrentToplevelSecurityInfo = aRequest;
395 : } else {
396 3 : mCurrentToplevelSecurityInfo = info;
397 : }
398 :
399 : // The subrequest counters are now in sync with mCurrentToplevelSecurityInfo,
400 : // don't restore after top level document load finishes.
401 3 : mRestoreSubrequests = false;
402 :
403 3 : UpdateSecurityState(aRequest, withNewLocation, withNewSink || updateStatus);
404 3 : }
405 :
406 : void
407 2 : nsSecureBrowserUIImpl::UpdateSubrequestMembers(nsISupports* securityInfo,
408 : nsIRequest* request)
409 : {
410 : // For wyciwyg channels in subdocuments we only update our
411 : // subrequest state members.
412 : uint32_t reqState = GetSecurityStateFromSecurityInfoAndRequest(securityInfo,
413 2 : request);
414 :
415 2 : if (reqState & STATE_IS_SECURE) {
416 : // do nothing
417 2 : } else if (reqState & STATE_IS_BROKEN) {
418 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
419 : ("SecureUI:%p: OnStateChange: subreq BROKEN\n", this));
420 0 : ++mSubRequestsBrokenSecurity;
421 : } else {
422 2 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
423 : ("SecureUI:%p: OnStateChange: subreq INSECURE\n", this));
424 2 : ++mSubRequestsNoSecurity;
425 : }
426 2 : }
427 :
428 : NS_IMETHODIMP
429 31 : nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
430 : nsIRequest* aRequest,
431 : uint32_t aProgressStateFlags,
432 : nsresult aStatus)
433 : {
434 31 : MOZ_ASSERT(NS_IsMainThread());
435 62 : ReentrancyGuard guard(*this);
436 : /*
437 : All discussion, unless otherwise mentioned, only refers to
438 : http, https, file or wyciwig requests.
439 :
440 :
441 : Redirects are evil, well, some of them.
442 : There are multiple forms of redirects.
443 :
444 : Redirects caused by http refresh content are ok, because experiments show,
445 : with those redirects, the old page contents and their requests will come to STOP
446 : completely, before any progress from new refreshed page content is reported.
447 : So we can safely treat them as separate page loading transactions.
448 :
449 : Evil are redirects at the http protocol level, like code 302.
450 :
451 : If the toplevel documents gets replaced, i.e. redirected with 302, we do not care for the
452 : security state of the initial transaction, which has now been redirected,
453 : we only care for the new page load.
454 :
455 : For the implementation of the security UI, we make an assumption, that is hopefully true.
456 :
457 : Imagine, the received page that was delivered with the 302 redirection answer,
458 : also delivered html content.
459 :
460 : What happens if the parser starts to analyze the content and tries to load contained sub objects?
461 :
462 : In that case we would see start and stop requests for subdocuments, some for the previous document,
463 : some for the new target document. And only those for the new toplevel document may be
464 : taken into consideration, when deciding about the security state of the next toplevel document.
465 :
466 : Because security state is being looked at, when loading stops for (sub)documents, this
467 : could cause real confusion, because we have to decide, whether an incoming progress
468 : belongs to the new toplevel page, or the previous, already redirected page.
469 :
470 : Can we simplify here?
471 :
472 : If a redirect at the http protocol level is seen, can we safely assume, its html content
473 : will not be parsed, anylzed, and no embedded objects will get loaded (css, js, images),
474 : because the redirect is already happening?
475 :
476 : If we can assume that, this really simplify things. Because we will never see notification
477 : for sub requests that need to get ignored.
478 :
479 : I would like to make this assumption for now, but please let me (kaie) know if I'm wrong.
480 :
481 : Excurse:
482 : If my assumption is wrong, then we would require more tracking information.
483 : We need to keep lists of all pointers to request object that had been seen since the
484 : last toplevel start event.
485 : If the start for a redirected page is seen, the list of releveant object must be cleared,
486 : and only progress for requests which start after it must be analyzed.
487 : All other events must be ignored, as they belong to now irrelevant previous top level documents.
488 :
489 :
490 : Frames are also evil.
491 :
492 : First we need a decision.
493 : kaie thinks:
494 : Only if the toplevel frame is secure, we should try to display secure lock icons.
495 : If some of the inner contents are insecure, we display mixed mode.
496 :
497 : But if the top level frame is not secure, why indicate a mixed lock icon at all?
498 : I think we should always display an open lock icon, if the top level frameset is insecure.
499 :
500 : That's the way Netscape Communicator behaves, and I think we should do the same.
501 :
502 : The user will not know which parts are secure and which are not,
503 : and any certificate information, displayed in the tooltip or in the "page info"
504 : will only be relevant for some subframe(s), and the user will not know which ones,
505 : so we shouldn't display it as a general attribute of the displayed page.
506 :
507 : Why are frames evil?
508 :
509 : Because the progress for the toplevel frame document is not easily distinguishable
510 : from subframes. The same STATE bits are reported.
511 :
512 : While at first sight, when a new page load happens,
513 : the toplevel frameset document has also the STATE_IS_NETWORK bit in it.
514 : But this can't really be used. Because in case that document causes a http 302 redirect,
515 : the real top level frameset will no longer have that bit.
516 :
517 : But we need some way to distinguish top level frames from inner frames.
518 :
519 : I saw that the web progress we get delivered has a reference to the toplevel DOM window.
520 :
521 : I suggest, we look at all incoming requests.
522 : If a request is NOT for the toplevel DOM window, we will always treat it as a subdocument request,
523 : regardless of whether the load flags indicate a top level document.
524 : */
525 :
526 62 : nsCOMPtr<mozIDOMWindowProxy> windowForProgress;
527 31 : aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
528 :
529 62 : nsCOMPtr<mozIDOMWindowProxy> window(do_QueryReferent(mWindow));
530 31 : MOZ_ASSERT(window, "Window has gone away?!");
531 :
532 31 : if (!mIOService) {
533 2 : mIOService = do_GetService(NS_IOSERVICE_CONTRACTID);
534 : }
535 :
536 31 : bool isNoContentResponse = false;
537 62 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
538 31 : if (httpChannel)
539 : {
540 : uint32_t response;
541 19 : isNoContentResponse = NS_SUCCEEDED(httpChannel->GetResponseStatus(&response)) &&
542 16 : (response == 204 || response == 205);
543 : }
544 31 : const bool isToplevelProgress = (windowForProgress.get() == window.get()) && !isNoContentResponse;
545 :
546 31 : if (windowForProgress)
547 : {
548 31 : if (isToplevelProgress)
549 : {
550 31 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
551 : ("SecureUI:%p: OnStateChange: progress: for toplevel\n", this));
552 : }
553 : else
554 : {
555 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
556 : ("SecureUI:%p: OnStateChange: progress: for something else\n", this));
557 : }
558 : }
559 : else
560 : {
561 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
562 : ("SecureUI:%p: OnStateChange: progress: no window known\n", this));
563 : }
564 :
565 31 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
566 : ("SecureUI:%p: OnStateChange\n", this));
567 :
568 31 : if (mIsViewSource) {
569 0 : return NS_OK;
570 : }
571 :
572 31 : if (!aRequest)
573 : {
574 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
575 : ("SecureUI:%p: OnStateChange with null request\n", this));
576 0 : return NS_ERROR_NULL_POINTER;
577 : }
578 :
579 31 : if (MOZ_LOG_TEST(gSecureDocLog, LogLevel::Debug)) {
580 0 : nsXPIDLCString reqname;
581 0 : aRequest->GetName(reqname);
582 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
583 : ("SecureUI:%p: %p %p OnStateChange %x %s\n", this, aWebProgress,
584 : aRequest, aProgressStateFlags, reqname.get()));
585 : }
586 :
587 62 : nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
588 :
589 62 : nsCOMPtr<nsIURI> uri;
590 62 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
591 31 : if (channel) {
592 25 : channel->GetURI(getter_AddRefs(uri));
593 : }
594 :
595 62 : nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(aRequest));
596 31 : if (imgRequest) {
597 0 : MOZ_ASSERT(!channel, "Request channel somehow not available");
598 : // for image requests, we get the URI from here
599 0 : imgRequest->GetURI(getter_AddRefs(uri));
600 : }
601 :
602 31 : if (uri) {
603 : bool vs;
604 25 : if (NS_SUCCEEDED(uri->SchemeIs("javascript", &vs)) && vs) {
605 : // We ignore the progress events for javascript URLs.
606 : // If a document loading gets triggered, we will see more events.
607 0 : return NS_OK;
608 : }
609 : }
610 :
611 31 : uint32_t loadFlags = 0;
612 31 : aRequest->GetLoadFlags(&loadFlags);
613 :
614 31 : if (aProgressStateFlags & STATE_START
615 10 : &&
616 10 : aProgressStateFlags & STATE_IS_REQUEST
617 10 : &&
618 : isToplevelProgress
619 10 : &&
620 10 : loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
621 : {
622 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
623 : ("SecureUI:%p: OnStateChange: SOMETHING STARTS FOR TOPMOST DOCUMENT\n", this));
624 : }
625 :
626 31 : if (aProgressStateFlags & STATE_STOP
627 16 : &&
628 16 : aProgressStateFlags & STATE_IS_REQUEST
629 10 : &&
630 : isToplevelProgress
631 10 : &&
632 10 : loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
633 : {
634 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
635 : ("SecureUI:%p: OnStateChange: SOMETHING STOPS FOR TOPMOST DOCUMENT\n", this));
636 : }
637 :
638 31 : bool isSubDocumentRelevant = true;
639 :
640 : // We are only interested in requests that load in the browser window...
641 31 : if (!imgRequest) { // is not imgRequest
642 62 : nsCOMPtr<nsIHttpChannel> httpRequest(do_QueryInterface(aRequest));
643 31 : if (!httpRequest) {
644 40 : nsCOMPtr<nsIFileChannel> fileRequest(do_QueryInterface(aRequest));
645 20 : if (!fileRequest) {
646 28 : nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
647 14 : if (!wyciwygRequest) {
648 28 : nsCOMPtr<nsIFTPChannel> ftpRequest(do_QueryInterface(aRequest));
649 14 : if (!ftpRequest) {
650 14 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
651 : ("SecureUI:%p: OnStateChange: not relevant for sub content\n", this));
652 14 : isSubDocumentRelevant = false;
653 : }
654 : }
655 : }
656 : }
657 : }
658 :
659 : // This will ignore all resource, chrome, data, file, moz-icon, and anno
660 : // protocols. Local resources are treated as trusted.
661 31 : if (uri && mIOService) {
662 : bool hasFlag;
663 : nsresult rv =
664 50 : mIOService->URIChainHasFlags(uri,
665 : nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
666 50 : &hasFlag);
667 25 : if (NS_SUCCEEDED(rv) && hasFlag) {
668 6 : isSubDocumentRelevant = false;
669 : }
670 : }
671 :
672 : #if defined(DEBUG)
673 62 : if (aProgressStateFlags & STATE_STOP
674 47 : &&
675 16 : channel)
676 : {
677 13 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
678 : ("SecureUI:%p: OnStateChange: seeing STOP with security state: %d\n", this,
679 : GetSecurityStateFromSecurityInfoAndRequest(securityInfo, aRequest)
680 : ));
681 : }
682 : #endif
683 :
684 31 : if (aProgressStateFlags & STATE_TRANSFERRING
685 5 : &&
686 5 : aProgressStateFlags & STATE_IS_REQUEST)
687 : {
688 : // The listing of a request in mTransferringRequests
689 : // means, there has already been data transfered.
690 5 : mTransferringRequests.Add(aRequest, fallible);
691 :
692 5 : return NS_OK;
693 : }
694 :
695 26 : bool requestHasTransferedData = false;
696 :
697 26 : if (aProgressStateFlags & STATE_STOP
698 16 : &&
699 16 : aProgressStateFlags & STATE_IS_REQUEST)
700 : {
701 10 : PLDHashEntryHdr* entry = mTransferringRequests.Search(aRequest);
702 10 : if (entry) {
703 5 : mTransferringRequests.RemoveEntry(entry);
704 5 : requestHasTransferedData = true;
705 : }
706 :
707 10 : if (!requestHasTransferedData) {
708 : // Because image loads doesn't support any TRANSFERRING notifications but
709 : // only START and STOP we must ask them directly whether content was
710 : // transferred. See bug 432685 for details.
711 : nsCOMPtr<nsISecurityInfoProvider> securityInfoProvider =
712 10 : do_QueryInterface(aRequest);
713 : // Guess true in all failure cases to be safe. But if we're not
714 : // an nsISecurityInfoProvider, then we just haven't transferred
715 : // any data.
716 : bool hasTransferred;
717 5 : requestHasTransferedData =
718 5 : securityInfoProvider &&
719 0 : (NS_FAILED(securityInfoProvider->GetHasTransferredData(&hasTransferred)) ||
720 5 : hasTransferred);
721 : }
722 : }
723 :
724 26 : bool allowSecurityStateChange = true;
725 26 : if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
726 : {
727 : // The original consumer (this) is no longer the target of the load.
728 : // Ignore any events with this flag, do not allow them to update
729 : // our secure UI state.
730 0 : allowSecurityStateChange = false;
731 : }
732 :
733 26 : if (aProgressStateFlags & STATE_START
734 10 : &&
735 10 : aProgressStateFlags & STATE_IS_REQUEST
736 10 : &&
737 : isToplevelProgress
738 10 : &&
739 10 : loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
740 : {
741 : int32_t saveSubBroken;
742 : int32_t saveSubNo;
743 6 : nsCOMPtr<nsIAssociatedContentSecurity> prevContentSecurity;
744 :
745 3 : int32_t newSubBroken = 0;
746 3 : int32_t newSubNo = 0;
747 :
748 3 : bool inProgress = (mDocumentRequestsInProgress != 0);
749 :
750 3 : if (allowSecurityStateChange && !inProgress) {
751 3 : saveSubBroken = mSubRequestsBrokenSecurity;
752 3 : saveSubNo = mSubRequestsNoSecurity;
753 3 : prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
754 : }
755 :
756 3 : if (allowSecurityStateChange && !inProgress)
757 : {
758 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
759 : ("SecureUI:%p: OnStateChange: start for toplevel document\n", this
760 : ));
761 :
762 3 : if (prevContentSecurity)
763 : {
764 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
765 : ("SecureUI:%p: OnStateChange: start, saving current sub state\n", this
766 : ));
767 :
768 : // before resetting our state, let's save information about
769 : // sub element loads, so we can restore it later
770 0 : prevContentSecurity->SetCountSubRequestsBrokenSecurity(saveSubBroken);
771 0 : prevContentSecurity->SetCountSubRequestsNoSecurity(saveSubNo);
772 0 : prevContentSecurity->Flush();
773 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: Saving subs in START to %p as %d,%d\n",
774 : this, prevContentSecurity.get(), saveSubBroken, saveSubNo));
775 : }
776 :
777 3 : bool retrieveAssociatedState = false;
778 :
779 3 : if (securityInfo &&
780 3 : (aProgressStateFlags & nsIWebProgressListener::STATE_RESTORING) != 0) {
781 0 : retrieveAssociatedState = true;
782 : } else {
783 6 : nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
784 3 : if (wyciwygRequest) {
785 0 : retrieveAssociatedState = true;
786 : }
787 : }
788 :
789 3 : if (retrieveAssociatedState)
790 : {
791 : // When restoring from bfcache, we will not get events for the
792 : // page's sub elements, so let's load the state of sub elements
793 : // from the cache.
794 :
795 : nsCOMPtr<nsIAssociatedContentSecurity>
796 0 : newContentSecurity(do_QueryInterface(securityInfo));
797 :
798 0 : if (newContentSecurity)
799 : {
800 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
801 : ("SecureUI:%p: OnStateChange: start, loading old sub state\n", this
802 : ));
803 :
804 0 : newContentSecurity->GetCountSubRequestsBrokenSecurity(&newSubBroken);
805 0 : newContentSecurity->GetCountSubRequestsNoSecurity(&newSubNo);
806 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: Restoring subs in START from %p to %d,%d\n",
807 : this, newContentSecurity.get(), newSubBroken, newSubNo));
808 : }
809 : }
810 : else
811 : {
812 : // If we don't get OnLocationChange for this top level load later,
813 : // it didn't get rendered. But we reset the state to unknown and
814 : // mSubRequests* to zeros. If we would have left these values after
815 : // this top level load stoped, we would override the original top level
816 : // load with all zeros and break mixed content state on back and forward.
817 3 : mRestoreSubrequests = true;
818 : }
819 : }
820 :
821 3 : if (allowSecurityStateChange && !inProgress) {
822 3 : ResetStateTracking();
823 3 : mSubRequestsBrokenSecurity = newSubBroken;
824 3 : mSubRequestsNoSecurity = newSubNo;
825 3 : mNewToplevelSecurityStateKnown = false;
826 : }
827 :
828 : // By using a counter, this code also works when the toplevel
829 : // document get's redirected, but the STOP request for the
830 : // previous toplevel document has not yet have been received.
831 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
832 : ("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this
833 : ));
834 3 : ++mDocumentRequestsInProgress;
835 :
836 3 : return NS_OK;
837 : }
838 :
839 23 : if (aProgressStateFlags & STATE_STOP
840 16 : &&
841 16 : aProgressStateFlags & STATE_IS_REQUEST
842 10 : &&
843 : isToplevelProgress
844 10 : &&
845 10 : loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
846 : {
847 6 : nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
848 :
849 3 : if (allowSecurityStateChange) {
850 3 : temp_ToplevelEventSink = mToplevelEventSink;
851 : }
852 :
853 3 : if (mDocumentRequestsInProgress <= 0) {
854 : // Ignore stop requests unless a document load is in progress
855 : // Unfortunately on application start, see some stops without having seen any starts...
856 0 : return NS_OK;
857 : }
858 :
859 3 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
860 : ("SecureUI:%p: OnStateChange: --mDocumentRequestsInProgress\n", this
861 : ));
862 :
863 3 : if (!temp_ToplevelEventSink && channel)
864 : {
865 2 : if (allowSecurityStateChange)
866 : {
867 2 : ObtainEventSink(channel, temp_ToplevelEventSink);
868 : }
869 : }
870 :
871 3 : bool sinkChanged = false;
872 : bool inProgress;
873 3 : if (allowSecurityStateChange) {
874 3 : sinkChanged = (mToplevelEventSink != temp_ToplevelEventSink);
875 3 : mToplevelEventSink = temp_ToplevelEventSink;
876 : }
877 3 : --mDocumentRequestsInProgress;
878 3 : inProgress = mDocumentRequestsInProgress > 0;
879 :
880 3 : if (allowSecurityStateChange && requestHasTransferedData) {
881 : // Data has been transferred for the single toplevel
882 : // request. Evaluate the security state.
883 :
884 : // Do this only when the sink has changed. We update and notify
885 : // the state from OnLacationChange, this is actually redundant.
886 : // But when the target sink changes between OnLocationChange and
887 : // OnStateChange, we have to fire the notification here (again).
888 :
889 1 : if (sinkChanged || mOnLocationChangeSeen) {
890 1 : EvaluateAndUpdateSecurityState(aRequest, securityInfo, false,
891 1 : sinkChanged);
892 1 : return NS_OK;
893 : }
894 : }
895 2 : mOnLocationChangeSeen = false;
896 :
897 2 : if (mRestoreSubrequests && !inProgress)
898 : {
899 : // We get here when there were no OnLocationChange between
900 : // OnStateChange(START) and OnStateChange(STOP). Then the load has not
901 : // been rendered but has been retargeted in some other way then by external
902 : // app handler. Restore mSubRequests* members to what the current security
903 : // state info holds (it was reset to all zero in OnStateChange(START)
904 : // before).
905 : nsCOMPtr<nsIAssociatedContentSecurity> currentContentSecurity(
906 4 : do_QueryInterface(mCurrentToplevelSecurityInfo));
907 :
908 : // Drop this indication flag, the restore operation is just being done.
909 2 : mRestoreSubrequests = false;
910 :
911 : // We can do this since the state didn't actually change.
912 2 : mNewToplevelSecurityStateKnown = true;
913 :
914 2 : int32_t subBroken = 0;
915 2 : int32_t subNo = 0;
916 :
917 2 : if (currentContentSecurity)
918 : {
919 0 : currentContentSecurity->GetCountSubRequestsBrokenSecurity(&subBroken);
920 0 : currentContentSecurity->GetCountSubRequestsNoSecurity(&subNo);
921 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: Restoring subs in STOP from %p to %d,%d\n",
922 : this, currentContentSecurity.get(), subBroken, subNo));
923 : }
924 :
925 2 : mSubRequestsBrokenSecurity = subBroken;
926 2 : mSubRequestsNoSecurity = subNo;
927 : }
928 :
929 2 : return NS_OK;
930 : }
931 :
932 20 : if (aProgressStateFlags & STATE_STOP
933 13 : &&
934 13 : aProgressStateFlags & STATE_IS_REQUEST)
935 : {
936 7 : if (!isSubDocumentRelevant)
937 5 : return NS_OK;
938 :
939 : // if we arrive here, LOAD_DOCUMENT_URI is not set
940 :
941 : // We only care for the security state of sub requests which have actually transfered data.
942 :
943 2 : if (allowSecurityStateChange && requestHasTransferedData)
944 : {
945 2 : UpdateSubrequestMembers(securityInfo, aRequest);
946 :
947 : // Care for the following scenario:
948 : // A new top level document load might have already started,
949 : // but the security state of the new top level document might not yet been known.
950 : //
951 : // At this point, we are learning about the security state of a sub-document.
952 : // We must not update the security state based on the sub content,
953 : // if the new top level state is not yet known.
954 : //
955 : // We skip updating the security state in this case.
956 :
957 2 : if (mNewToplevelSecurityStateKnown) {
958 2 : UpdateSecurityState(aRequest, false, false);
959 : }
960 : }
961 :
962 2 : return NS_OK;
963 : }
964 :
965 13 : return NS_OK;
966 : }
967 :
968 : // I'm keeping this as a separate function, in order to simplify the review
969 : // for bug 412456. We should inline this in a follow up patch.
970 2 : void nsSecureBrowserUIImpl::ObtainEventSink(nsIChannel *channel,
971 : nsCOMPtr<nsISecurityEventSink> &sink)
972 : {
973 2 : if (!sink)
974 2 : NS_QueryNotificationCallbacks(channel, sink);
975 2 : }
976 :
977 : void
978 5 : nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest,
979 : bool withNewLocation,
980 : bool withUpdateStatus)
981 : {
982 5 : lockIconState newSecurityState = lis_no_security;
983 5 : if (mNewToplevelSecurityState & STATE_IS_SECURE) {
984 : // If a subresoure/request was insecure, then we have mixed security.
985 0 : if (mSubRequestsBrokenSecurity || mSubRequestsNoSecurity) {
986 0 : newSecurityState = lis_mixed_security;
987 : } else {
988 0 : newSecurityState = lis_high_security;
989 : }
990 : }
991 :
992 5 : if (mNewToplevelSecurityState & STATE_IS_BROKEN) {
993 0 : newSecurityState = lis_broken_security;
994 : }
995 :
996 5 : mCertUserOverridden =
997 5 : mNewToplevelSecurityState & STATE_CERT_USER_OVERRIDDEN;
998 :
999 5 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
1000 : ("SecureUI:%p: UpdateSecurityState: old-new %d - %d\n", this,
1001 : mNotifiedSecurityState, newSecurityState));
1002 :
1003 5 : bool flagsChanged = false;
1004 5 : if (mNotifiedSecurityState != newSecurityState) {
1005 : // Something changed since the last time.
1006 0 : flagsChanged = true;
1007 0 : mNotifiedSecurityState = newSecurityState;
1008 :
1009 : // If we have no security, we also shouldn't have any SSL status.
1010 0 : if (newSecurityState == lis_no_security) {
1011 0 : mSSLStatus = nullptr;
1012 : }
1013 : }
1014 :
1015 5 : if (mNotifiedToplevelIsEV != mNewToplevelIsEV) {
1016 0 : flagsChanged = true;
1017 0 : mNotifiedToplevelIsEV = mNewToplevelIsEV;
1018 : }
1019 :
1020 5 : if (flagsChanged || withNewLocation || withUpdateStatus) {
1021 2 : TellTheWorld(aRequest);
1022 : }
1023 5 : }
1024 :
1025 : void
1026 2 : nsSecureBrowserUIImpl::TellTheWorld(nsIRequest* aRequest)
1027 : {
1028 2 : uint32_t state = STATE_IS_INSECURE;
1029 2 : GetState(&state);
1030 :
1031 2 : if (mToplevelEventSink) {
1032 1 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
1033 : ("SecureUI:%p: UpdateSecurityState: calling OnSecurityChange\n",
1034 : this));
1035 :
1036 1 : mToplevelEventSink->OnSecurityChange(aRequest, state);
1037 : } else {
1038 1 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
1039 : ("SecureUI:%p: UpdateSecurityState: NO mToplevelEventSink!\n",
1040 : this));
1041 :
1042 : }
1043 2 : }
1044 :
1045 : NS_IMETHODIMP
1046 2 : nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
1047 : nsIRequest* aRequest,
1048 : nsIURI* aLocation,
1049 : uint32_t aFlags)
1050 : {
1051 2 : MOZ_ASSERT(NS_IsMainThread());
1052 4 : ReentrancyGuard guard(*this);
1053 :
1054 2 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
1055 : ("SecureUI:%p: OnLocationChange\n", this));
1056 :
1057 2 : bool updateIsViewSource = false;
1058 2 : bool temp_IsViewSource = false;
1059 4 : nsCOMPtr<mozIDOMWindowProxy> window;
1060 :
1061 2 : if (aLocation)
1062 : {
1063 : bool vs;
1064 :
1065 2 : nsresult rv = aLocation->SchemeIs("view-source", &vs);
1066 2 : NS_ENSURE_SUCCESS(rv, rv);
1067 :
1068 2 : if (vs) {
1069 0 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
1070 : ("SecureUI:%p: OnLocationChange: view-source\n", this));
1071 : }
1072 :
1073 2 : updateIsViewSource = true;
1074 2 : temp_IsViewSource = vs;
1075 : }
1076 :
1077 2 : if (updateIsViewSource) {
1078 2 : mIsViewSource = temp_IsViewSource;
1079 : }
1080 2 : mCurrentURI = aLocation;
1081 2 : window = do_QueryReferent(mWindow);
1082 2 : MOZ_ASSERT(window, "Window has gone away?!");
1083 :
1084 : // When |aRequest| is null, basically we don't trust that document. But if
1085 : // docshell insists that the document has not changed at all, we will reuse
1086 : // the previous security state, no matter what |aRequest| may be.
1087 2 : if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT)
1088 0 : return NS_OK;
1089 :
1090 : // The location bar has changed, so we must update the security state. The
1091 : // only concern with doing this here is that a page may transition from being
1092 : // reported as completely secure to being reported as partially secure
1093 : // (mixed). This may be confusing for users, and it may bother users who
1094 : // like seeing security dialogs. However, it seems prudent given that page
1095 : // loading may never end in some edge cases (perhaps by a site with malicious
1096 : // intent).
1097 :
1098 4 : nsCOMPtr<mozIDOMWindowProxy> windowForProgress;
1099 2 : aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
1100 :
1101 4 : nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
1102 :
1103 2 : if (windowForProgress.get() == window.get()) {
1104 : // For toplevel channels, update the security state right away.
1105 2 : mOnLocationChangeSeen = true;
1106 2 : EvaluateAndUpdateSecurityState(aRequest, securityInfo, true, false);
1107 2 : return NS_OK;
1108 : }
1109 :
1110 : // For channels in subdocuments we only update our subrequest state members.
1111 0 : UpdateSubrequestMembers(securityInfo, aRequest);
1112 :
1113 : // Care for the following scenario:
1114 :
1115 : // A new toplevel document load might have already started, but the security
1116 : // state of the new toplevel document might not yet be known.
1117 : //
1118 : // At this point, we are learning about the security state of a sub-document.
1119 : // We must not update the security state based on the sub content, if the new
1120 : // top level state is not yet known.
1121 : //
1122 : // We skip updating the security state in this case.
1123 :
1124 0 : if (mNewToplevelSecurityStateKnown) {
1125 0 : UpdateSecurityState(aRequest, true, false);
1126 : }
1127 :
1128 0 : return NS_OK;
1129 : }
1130 :
1131 : NS_IMETHODIMP
1132 0 : nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress*, nsIRequest*, nsresult,
1133 : const char16_t*)
1134 : {
1135 0 : MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
1136 : return NS_OK;
1137 : }
1138 :
1139 : nsresult
1140 1 : nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress* aWebProgress,
1141 : nsIRequest* aRequest,
1142 : uint32_t state)
1143 : {
1144 1 : MOZ_ASSERT(NS_IsMainThread());
1145 : #if defined(DEBUG)
1146 2 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
1147 1 : if (!channel)
1148 0 : return NS_OK;
1149 :
1150 2 : nsCOMPtr<nsIURI> aURI;
1151 1 : channel->GetURI(getter_AddRefs(aURI));
1152 :
1153 1 : if (aURI) {
1154 1 : MOZ_LOG(gSecureDocLog, LogLevel::Debug,
1155 : ("SecureUI:%p: OnSecurityChange: (%x) %s\n", this,
1156 : state, aURI->GetSpecOrDefault().get()));
1157 : }
1158 : #endif
1159 :
1160 1 : return NS_OK;
1161 : }
1162 :
1163 : // nsISSLStatusProvider methods
1164 : NS_IMETHODIMP
1165 1 : nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result)
1166 : {
1167 1 : NS_ENSURE_ARG_POINTER(_result);
1168 1 : MOZ_ASSERT(NS_IsMainThread());
1169 :
1170 1 : switch (mNotifiedSecurityState)
1171 : {
1172 : case lis_broken_security:
1173 : case lis_mixed_security:
1174 : case lis_high_security:
1175 0 : break;
1176 :
1177 : default:
1178 0 : MOZ_FALLTHROUGH_ASSERT("if this is reached you must add more entries to the switch");
1179 : case lis_no_security:
1180 1 : *_result = nullptr;
1181 1 : return NS_OK;
1182 : }
1183 :
1184 0 : *_result = mSSLStatus;
1185 0 : NS_IF_ADDREF(*_result);
1186 :
1187 0 : return NS_OK;
1188 9 : }
|