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