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