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