Files
palemoon27/security/manager/ssl/nsSecureBrowserUIImpl.cpp
roytam1 fb49d72bb0 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1252882 - Content-Signature Service - some tests r=keeler,r=fkiefer (7fc1f726a4)
- Bug 1265085 - Replace verification source with a SAN in the content signature verifier interface. r=Cykesiopka,r=fkiefer (0881ba797d)
- Bug 1264706: Move nsILocalCertService, and implementation, to security/manager/ssl in order to alloow use w use elsewhere in gecko. r=dkeeler (6f2200f2a6)
- Bug 1219088 - Clear the session cache when a weak crypto override is revoked. r=keeler (456e2ce3e6)
- Bug 550185 - Ensure nsCertTree::GetCellText returns an initialized value. r=kaie (ee356452a5)
- Bug 1252384 - Remove nsICertTree.isHostPortOverride(). r=dkeele It is unused since the changes in Bug 825583 landed. (61400adad7)
- reorder as Bug 1411458 (600fc338a9)
- bug 1264761 - improve handling of x509 versions in certificate manager r=Cykesiopka (a89237c66d)
- Bug 1263857 - (followup) Disable windows crash reporter on automated tests. r=sfink (bb9581fec9)
- bug 1263221 - improve how PSM handles the visibility of __CERT_AddTempCertToPerm r=chmanchester,mgoodwin (d9fd09d9a6)
- bug 1182742 - allow users to override small key size errors r=rbarnes (b09074987b)
- Bug 1252722 - Fully implement nsNSSShutDownObject everywhere. r=keeler (8c1a8df597)
- Bug 1252722 - Use smart pointers for NSS resources. r=keeler (b2ef34f9d2)
- Bug 1252722 - Ensure arguments of all public methods are checked. r=keeler (7064697e25)
- bug 1242032 - change some pipnss logging output from Debug to Verbose r=Cykesiopka (516c52da9f)
- Bug 1252722 - Improve handling of PK11_* function error codes. r=keeler (b47d13bd7e)
- Bug 1251801 - Ensure arguments of all public methods are checked. r=keeler (ffe77174e7)
- Bug 1127158 - Remove brittle debug only flag math in nsSecureBrowserUIImpl.cpp. r=dkeeler (410d25dc3e)
- Bug 1257246: Update security/manager for eslint 2. r=cykesiopka (bbdcf78264)
- bug 1218515 - flip pinning-test.badssl.com into production mode r=jcj DONTBUILD NPOTB (0d93e78cab)
- Bug 1199624 - Don't use memset and memcmp in files that don't include cstring explicitly. r=briansmith (5a938e056c)
- Bug 1154399 - Part 1: De-templatize and un-inline IntegralValue. r=keeler (bcaa11a646)
- Bug 1154399 - Part 2: Simplify and un-inline OptionalVersion. r=keeler (a5a9bb5e46)
- Bug 1154399 - Part 3: Simplify OptionalExtensions. r=keeler (0d3f613cde)
- Bug 1154399 - Part 4: Simplify certificate parsing in OCSP responses. r=keeler (94aa90a96d)
- Bug 1189020 - Replace |// unnamed namespace| with |// namespace| in mozilla::pkix. r=Cykesiopka (bb016e13ac)
- bug 1255153 - (re)move redundant xpcshell name constraint tests to gtests r=Cykesiopka,jcj (d8597a3bb1)
- bug 1248099 - add extended key usage tests for mozilla::pkix r=Cykesiopka,jcj (f9c11a8ecc)
- Bug 1266298 - Add sys_fchmod to seccomp whitelist r=jld (e16608d738)
- Bug 1268579 - Add inotify_rm_watch to the seccomp-bpf whitelist. r=jld (c20823e237)
- Bug 1176099 - Add hooks for sigprocmask/pthread_sigmask. r=jld r=glandium (55d16a8e41)
- Bug 1176099 - Fix missing NULL check r=luke (2f6e6e3836)
- Bug 1245789 - Use ifdef MOZ_WIDEVINE_EME to prevent compilation when not enabled. r=gerald (40d13ca2d9)
- Bug 1267453 - Amazon Widevine rejects HDCP on MacBook Pro with or without an external display. r=gcp (5366006c54)
- Bug 1268379 - Delay WMF checks in GMPParent - r=jesup (0f6642fdcc)
- bit of Bug 1245789 - Push detection of WMF decoding (e60b0634de)
- missing bit of 1267453 (573b867a62)
- Bug 1243594 (part 3) - leave the utf-8 encoding of the payload to rest.js instead of directly in loop. r=Standard8 (7781df1275)
- Bug 1243594 (part 2) - have rest.js automatically encode the request body as utf-8. r=gfritzsche (fd98dddf43)
- Bug 1246938 - Allow extra headers to be passed via hawk requests. r=markh (3692244524)
- Bug 1239354: Replace old-style generator function with star functions. r=asuth (a7793a89aa)
- Bug 1217982 - Remove for-each from storage/. r=mak (bbff0f0b7b)
- Bug 1223510, part 1 - Always finishTest() in test_nonUnicode.html. r=baku (ba3fb681ad)
- Bug 1223510, part 2 - Make ArchiveReader tests use pushPrefEnv. r=baku (5a4a5f9a7a)
- Bug 1223510, part 3 - Change where generator is created. r=baku Mostly this lets us take advantage of things created during the initial setup. (ee337d83de)
- Bug 1220304 - Part 1 - Make test_XHRSendData.html use SpecialPowers.createFiles(). r=baku (91fc25f3b7)
- Bug 1220304 - Part 2 - Make ArchiveReader tests use SpecialPowers.createFiles(). r=baku (5a0bfc4200)
- Bug 1267966 - Remove the deprecation warning from Attr.ownerElement; r=baku (cfd8910f2f)
- Bug 1269646 - Console API should be NOP after window-inner-destroyed, r=smaug (c4e5959d1f)
- Bug 1263392 - ConsoleCallData::mStatus should be set also when ConsoleCallData is not used in workers, r=smaug (10358e33a1)
- Bug 1268361 - Strip leading '?' in new URLSearchParams(query), r=smaug (a1a5075185)
- Bug 1052139 - Make more objects on the global prototype chain have immutable [[Prototype]], when we enable enforcement of this requirement. r=bz (8e48cfc3d6)
- Bug 1267932 - Update EventSource.webidl, r=ehsan (41532a54d5)
- Bug 1269383 - Remove dom.server-events.enabled pref, r=smaug (21e65b8436)
- Bug 1237077 - Part 3: use createCodebasePrincipal. r=tanvi (7da7268d47)
- Bug 1268721, part 1 - Use early continue in TransferableToIPCTransferable. r=jimm (317ab04f38)
- Bug 1268721, part 2 - Null check first argument to nsContentUtils::GetSurfaceData(). r=jimm (544d181021)
- Bug 1272203 (part 3) - Use NotNull in nsContentUtils::GetSurfaceData(). r=froydnj. (65e488c4f8)
- Bug 1272203 (part 1) - Add mozilla::NotNull to MFBT. r=froydnj. (4653d120fc)
- Bug 964092: don't let DOM DataChannels get GC'd if they have an active callback r=smaug,jib (c9c291f44a)
- Bug 1224186: Implement DOMTokenlist.replace r=baku,Ms2ger (329f4f942e)
- Bug 1265715 - Part 1. Pull Mode out of nsDisplayListBuilder; r=jfkthame (2c7cae0f3a)
- Bug 1265715 - Part 2. Add nsDisplayListBuilderMode parameter into nsLayoutUtils::PaintFrame; r=jfkthame (5ecabbda5d)
- Bug 1264949 - Ensure that the display list does not contain any background-image/background-color display item; r=jfkthame (018a7aec15)
- Bug 1265715 - Part 3. Use nsLayoutUtils::PaintFrame in ClipBackgroundByText; r=jfkthame (182a700fa6)
- Bug 1265715 - Part 4. Fix transform problem; r=jfkthame (37f77bf24b)
- Bug 1265715 - Part 5. bg-clip:text transform reftest; r=jfkthame (44778bcfd7)
- Bug 1267209 - Convert nsLayoutUtils::PaintFrame flags to be an enum class. r=jfkthame (4f304b84f1)
- Bug 1265280 - Temporary debugging code to crash with a useful abort message. r=khuey (43d532166f)
- Bug 1232939 - Ensure the opaque region of a fixed background layer is correctly clipped. r=mstange (cc2118e0a0)
- Bug 735857 - Treat background-attachment:fixed as background-attachment:scroll if it's on a non-root element affected by a transform. r=mstange (273d62aabf)
- Bug 735857 - Factor out a helper function nsLayoutUtils::IsTransformed(). r=mstange (61528fafef)
- Bug 1263286 - Move base-uri CSP check into SetBaseURIUsingFirstBaseWithHref. r=bz (03114b2fcb)
- Bug 1227327 - Allow specifying a background rect for background dislay items. r=mattwoodrow (410ef269eb)
- Bug 1227327 - Make fieldset frames build nsDisplayBackgroundImage items. r=mattwoodrow (9a3a8953b4)
- Fix temporary debugging patch for bug 1265280 so we'll hit the condition. r=khuey (fd30f8f0b4)
- Bug 1265715 - followup - Correct dirty region; r=me (38fc76e698)
- Bug 550426 - Use background-position-x/y in ActiveLayerTracker. r=dbaron (4f154a39dd)
- Bug 1266131 part 1 - [css-grid] 'order' doesn't apply to grid-aligned abs.pos. descendants (anymore). r=dholbert (ba8aa18fea)
- Bug 1266131 part 2 - [css-grid] Remove unused nsDisplayList::SortByCSSOrder() function. r=dholbert (b676c48a26)
- Bug 550426 - Add support for {background,mask}-position-{x,y}, most of the style system changes. r=dbaron (3739a8ec58)
- Bug 550426 - Add support for {background,mask}-position-{x,y}, StyleAnimation changes. r=dbaron (0fd2f97a60)
- Bug 852754 - Part 4: Reduce max downscaling allowed to <3. r=mstange (399b851221)
- Bug 1266868, part 1 - Fix nsCSSValue::Array leaks in the StyleAnimationValue code. r=dholbert (c6fc4f7d9c)
- Bug 1266868, part 2 - Fix leaks of the values passed to nsCSSValue::.SetPairValue in the StyleAnimationValue code. r=dholbert (e5a1ff8603)
- Bug 1266868, part 3 - Avoid Maybe::ref() where not necessary. r=dholbert (8dd435fd5e)
- Back out bug 1164227, because bug 1236043 fixes the original problem in a better way. (6b734f0718)
- Bug 1267524 Part 1 - Use member initializer list for nsStyleOutline. r=heycam (d7cabb2ea8)
- Bug 1267524 Part 2 - Use member initializer list for nsStyleXUL. r=heycam (82107506a6)
- Bug 1267524 Part 3 - Use member initializer list for nsStyleColumn. r=heycam (80318b0056)
- Bug 1267524 Part 4.1 - Add Reset() and rewrite methods for nsStyleSVGPaint. r=heycam (02ba8762cb)
- Bug 1267524 Part 4.2 - Use member initializer list for nsStyleSVG. r=heycam (5531ed4a93)
- Bug 1267524 Part 5 - Use member initializer list for nsStyleSVGReset. r=heycam (4a72005b1b)
- Bug 1267524 Part 6 - Use member initializer list for nsStylePosition. r=heycam (cfd6a8b640)
- Bug 1267524 Part 7 - Use member initializer list for nsStyleTable. r=heycam (510678ed8b)
- Bug 1267524 Part 8 - Use member initializer list for nsStyleTableBorder. r=heycam (c1617af193)
- Bug 1267524 Part 9 - Use member initializer list for nsStyleColor. r=heycam (753afba9f8)
- Bug 1267524 Part 10 - Use member initializer list for nsStyleDisplay. r=heycam (a6cc7ce52b)
- Bug 1267524 Part 11 - Use member initializer list for nsStyleVisibility. r=heycam (c93d75480e)
- Bug 1267524 Part 12 - Use member initializer list for nsStyleContent. r=heycam (ccc17aa74a)
- Bug 1267524 Part 13 - Use member initializer list for nsStyleTextReset. r=heycam (fc8b6ae837)
- Bug 1267524 Part 14 - Use member initializer list for nsStyleText. r=heycam (d73abb7d32)
- Bug 1267524 Part 15 - Use member initializer list for nsStyleUserInterface. r=heycam (3964558f27)
- Bug 1267524 Part 16 - Use member initializer list for nsStyleUIReset. r=heycam (3154cbc7d2)
- Bug 1267524 Part 17 - Use member initializer list for nsStyleVariables. r=heycam (3d286d2299)
- Bug 1267524 Part 18 - Remove "void" from zero argument functions. r=heycam (57b1a87c19)
- Bug 1267524 Part 19 - Move nsStyleCoord members to initializer list. r=heycam (385231a406)
- Bug 1227327 - Invalidate table parts and MathML frames when background-position changes on them. r=dbaron (088fad2be7)
- Bug 1268290: stylo: Pass SheetParsingMode to Servo, r=bholley (f05d51b7b1)
- Bug 1267833 - Pass the RawServoStyleSet to Servo_GetComputedValuesForAnonymousBox. r=heycam (c4870e2005)
- Bug 1268392 - Make Servo_GetComputedValues take a node rather than an element. r=bholley (61230bdc1f)
- Bug 1268390 - Part 1: Factor out most of nsStyleSet::AddDocStyleSheet for re-use. r=bholley (5bd89657a1)
- Bug 1268390 - Part 2: Add bindings for Servo_InsertStyleSheetBefore. r=bholley (f83ea77d37)
- Bug 1268404 - Part 1: Split out ResolveStyleForText from ResolveStyleForNonElement and pass in the text node. r=bholley (dc40bbc9dc)
- Bug 1268404 - Part 2: Implement ServoStyleSet::ResolveStyleForText. r=bholley (0c6bffbd4b)
- Bug 1268748 - Implement {Resolve,Probe}PseudoElementStyle. r=heycam (cd674703d6)
- Bug 1267560 - Get style structs from ServoComputedValues rather than the rule node, when using the Servo-backed style system. r=bholley (62784ed0ee)
- Bug 1268290 followup: remove stray semicolon on a CLOSED TREE. (609540fab1)
- Bug 1268390 - Part 3: Add support for doc style sheets in ServoStyleSet. r=bholley (787cee0d54)
- Bug 1267564 - Implement a couple of Servo-backed style object methods. r=bholley (b27b0f78a0)
- Bug 1250820 - Part 1: Define scoped enum for CSSPseudoClass::Type. r=heycam (c2992f4c01)
- Bug 1250820 - Part 2: Replace nsCSSPseudoClasses::Type with CSSPseudoClassType. r=heycam (8102ab491b)
- Bug 1250820 - Part 3: Replace notPseudo with negation. r=heycam (94f4b95650)
- Bug 1250820 - Part 4: Add MAX to CSSPseudoClassType. r=heycam (85acf2bc45)
- Bug 1206961 - Use channel->AsyncOpen2() for imageLoader; Remove security checks from callsites (r=bz) (0d5b91ca12)
- Bug 1134163 - Part1.Modify animationstart event timing in order to fire event after end of pending task. r=birtles (e2c333fb8d)
- Bug 1134163 - Part2 - Modify animation tests which rely on animationstart timing. r=birtles (fb780f4298)
- Bug 1067769 - Part 1: Avoid doing RequestRestyle and mutation batch for null target. r=birtles (c3a0c1a1ef)
- Bug 1067769 - Part 2: Support nullable target in KeyframeEffect(ReadOnly) constructor. r=birtles (e8ac02ebf0)
- Bug 1067769 - Part 3: Test for KeyframeEffectReadOnly with null target. r=birtles (9ee7fc48c3)
- Bug 1067769 - Part 4: Add some simple tests for document.getAnimation() in wpt. r=birtles (77c18ad32a)
- Bug 1067769 - Part 5: Support setting KeyframeEffect.target webidl interface. r=smaug (cb450cd6f7)
- Bug 1067769 - Part 6: Rename NonOwningAnimationTarget.h to AnimationTarget.h. r=birtles (45083b4141)
- Bug 1067769 - Part 7: Define OwningAnimationTarget and use it. r=birtles (0a716665aa)
- Bug 1067769 - Part 8: Add ConvertTarget function. r=birtles (2663246043)
- Bug 1067769 - Part 9: Wrap RequestRestyle and UnregisterTarget. r=birtles (1deb75c7e0)
- Bug 1067769 - Part 10: Implement SetTarget(). r=birtles (0823f6da17)
- Bug 1067769 - Part 11: Implement animation mutation observer while setting the target. r=birtles (8224724c49)
- Bug 1067769 - Part 12: Use Maybe<OwningAnimationTarget> in KeyframeEffect(ReadOnly) constructors. r=birtles (e057c15804)
- Bug 1067769 - Part 13: Test for setting the target in basic cases. r=birtles (b6a638a268)
- Bug 1067769 - Part 14: Test for our animation mutation observer. r=birtles (5381522d25)
- Bug 1264067 - [css-grid] 'fr' min-sizing is now invalid. r=dholbert (abc7d63364)
- Bug 550426 - Add support for {background,mask}-position-{x,y}, computed style additions. r=dbaron (0dea650527)
- Bug 1266948 - text-decoration-color: currentcolor should not use value from -webkit-text-fill-color; r=jfkthame (9b36b2f493)
- Bug 1271590 - Rename timespecadd to moz_timespecadd. r=jandem (4b417dabae)
- Bug 550426 - Use background-position-x/y when detecting scroll-linked effects. r=dbaron (478331b348)
- Bug 1227327 - Use regular background drawing for XUL groupbox frames. r=mattwoodrow (db42359656)
- Bug 1260329 - Properly escape the frameTable when running |dmd.py --clamp-contents|. r=mccr8. (b40a5a0f49)
- Bug 1148544 - Update tests to work with new way of handling user agent overrides. r=jchen (2cede65d5b)
- Bug 1262326 - Make test_user_agent_overrides.html work in e10s r=nwgh (81c4d7ba00)
- Bug 1180107: Factor out logic for determining whether a flex item's main size could influence cross size. r=mats (94b89305ea)
- Bug 1267471 - Check the snap info when comparing scroll metadata for equality. rs=botond (e5a40f0387)
- Bug 1257288 - Improve the APZ gtest infrastructure to make writing multi-FrameMetrics tests easier. r=kats (b4b898abc2)
- Bug 1256344 - Add a gtest to catch scenarios where the long-press block is interrupted by a non-touch block. r=botond (84982b1ba7)
- Bug 1265510 - Add a gtest for interrupting a scroll snap. r=botond (5f33cdadea)
- Bug 1246290 - Add a simple gtest to exercise the force-disabled-APZ codepaths. r=botond (be91113c70)
- Bug 1267470 - Move more fields from FrameMetrics to ScrollMetadata. r=kats (259f44ab15)
- Bug 1030952 part 4: For flex items with an aspect ratio, stomp on reflow state's main size *and cross size* in final reflow. r=mats (3f02ed9761)
- Bug 550426 - In PropertySupportsVariant, add {background,mask}-position-{x,y} to the list of properties that are parsed by functions. r=dbaron (b350dd9ec4)
- Bug 1258609: Initialize nsICanvasRenderingContextInternal with a DrawTarget instead of a gfxASurface. r=jrmuizel (236656c82d)
- Merge remote-tracking branch 'upstream/dev' into winbuild (c0659b547d)
- [mfbt] NotNull: VC2013 fix (86139057b8)
- layout: put back array initializations back to function body, fix VC2013 build. (3ac23f6474)
2024-09-06 09:12:45 +08:00

1242 lines
40 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nspr.h"
#include "mozilla/Logging.h"
#include "nsISecureBrowserUI.h"
#include "nsSecureBrowserUIImpl.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsCURILoader.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocument.h"
#include "nsIDOMElement.h"
#include "nsPIDOMWindow.h"
#include "nsIWebProgress.h"
#include "nsIWebProgressListener.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsIFileChannel.h"
#include "nsIWyciwygChannel.h"
#include "nsIFTPChannel.h"
#include "nsITransportSecurityInfo.h"
#include "nsISSLStatus.h"
#include "nsIURI.h"
#include "nsISecurityEventSink.h"
#include "nsISecurityInfoProvider.h"
#include "imgIRequest.h"
#include "nsThreadUtils.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsCRT.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIProtocolHandler.h"
using namespace mozilla;
//
// Log module for nsSecureBrowserUI logging...
//
// To enable logging (see prlog.h for full details):
//
// set NSPR_LOG_MODULES=nsSecureBrowserUI:5
// set NSPR_LOG_FILE=nspr.log
//
// this enables LogLevel::Debug level information and places all output in
// the file nspr.log
//
LazyLogModule gSecureDocLog("nsSecureBrowserUI");
struct RequestHashEntry : PLDHashEntryHdr {
void *r;
};
static bool
RequestMapMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
{
const RequestHashEntry *entry = static_cast<const RequestHashEntry*>(hdr);
return entry->r == key;
}
static void
RequestMapInitEntry(PLDHashEntryHdr *hdr, const void *key)
{
RequestHashEntry *entry = static_cast<RequestHashEntry*>(hdr);
entry->r = (void*)key;
}
static const PLDHashTableOps gMapOps = {
PLDHashTable::HashVoidPtrKeyStub,
RequestMapMatchEntry,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub,
RequestMapInitEntry
};
#ifdef DEBUG
class nsAutoAtomic {
public:
explicit nsAutoAtomic(Atomic<int32_t> &i)
:mI(i) {
mI++;
}
~nsAutoAtomic() {
mI--;
}
protected:
Atomic<int32_t> &mI;
private:
nsAutoAtomic(); // not accessible
};
#endif
nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
: mNotifiedSecurityState(lis_no_security)
, mNotifiedToplevelIsEV(false)
, mNewToplevelSecurityState(STATE_IS_INSECURE)
, mNewToplevelIsEV(false)
, mNewToplevelSecurityStateKnown(true)
, mIsViewSource(false)
, mSubRequestsBrokenSecurity(0)
, mSubRequestsNoSecurity(0)
, mCertUserOverridden(false)
, mRestoreSubrequests(false)
, mOnLocationChangeSeen(false)
#ifdef DEBUG
, mOnStateLocationChangeReentranceDetection(0)
#endif
, mTransferringRequests(&gMapOps, sizeof(RequestHashEntry))
{
MOZ_ASSERT(NS_IsMainThread());
ResetStateTracking();
}
NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
nsISecureBrowserUI,
nsIWebProgressListener,
nsISupportsWeakReference,
nsISSLStatusProvider)
NS_IMETHODIMP
nsSecureBrowserUIImpl::Init(nsIDOMWindow* aWindow)
{
MOZ_ASSERT(NS_IsMainThread());
if (MOZ_LOG_TEST(gSecureDocLog, LogLevel::Debug)) {
nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: Init: mWindow: %p, aWindow: %p\n", this,
window.get(), aWindow));
}
if (!aWindow) {
NS_WARNING("Null window passed to nsSecureBrowserUIImpl::Init()");
return NS_ERROR_INVALID_ARG;
}
if (mWindow) {
NS_WARNING("Trying to init an nsSecureBrowserUIImpl twice");
return NS_ERROR_ALREADY_INITIALIZED;
}
nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(aWindow));
if (pwin->IsInnerWindow()) {
pwin = pwin->GetOuterWindow();
}
nsresult rv;
mWindow = do_GetWeakReference(pwin, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsPIDOMWindow> piwindow(do_QueryInterface(aWindow));
if (!piwindow) return NS_ERROR_FAILURE;
nsIDocShell *docShell = piwindow->GetDocShell();
// The Docshell will own the SecureBrowserUI object
if (!docShell)
return NS_ERROR_FAILURE;
docShell->SetSecurityUI(this);
/* GetWebProgress(mWindow) */
// hook up to the webprogress notifications.
nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
if (!wp) return NS_ERROR_FAILURE;
/* end GetWebProgress */
wp->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
nsIWebProgress::NOTIFY_STATE_ALL |
nsIWebProgress::NOTIFY_LOCATION |
nsIWebProgress::NOTIFY_SECURITY);
return NS_OK;
}
NS_IMETHODIMP
nsSecureBrowserUIImpl::GetState(uint32_t* aState)
{
MOZ_ASSERT(NS_IsMainThread());
return MapInternalToExternalState(aState, mNotifiedSecurityState,
mNotifiedToplevelIsEV);
}
// static
already_AddRefed<nsISupports>
nsSecureBrowserUIImpl::ExtractSecurityInfo(nsIRequest* aRequest)
{
nsCOMPtr<nsISupports> retval;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
if (channel)
channel->GetSecurityInfo(getter_AddRefs(retval));
if (!retval) {
nsCOMPtr<nsISecurityInfoProvider> provider(do_QueryInterface(aRequest));
if (provider)
provider->GetSecurityInfo(getter_AddRefs(retval));
}
return retval.forget();
}
nsresult
nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconState lock, bool ev)
{
NS_ENSURE_ARG(aState);
switch (lock)
{
case lis_broken_security:
*aState = STATE_IS_BROKEN;
break;
case lis_mixed_security:
*aState = STATE_IS_BROKEN;
break;
case lis_high_security:
*aState = STATE_IS_SECURE | STATE_SECURE_HIGH;
break;
default:
case lis_no_security:
*aState = STATE_IS_INSECURE;
break;
}
if (ev && (*aState & STATE_IS_SECURE))
*aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
if (mCertUserOverridden && (*aState & STATE_IS_SECURE)) {
*aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
}
nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
if (!docShell)
return NS_OK;
// For content docShell's, the mixed content security state is set on the root docShell.
if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(docShell));
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
docShell = do_QueryInterface(sameTypeRoot);
if (!docShell)
return NS_OK;
}
// Has a Mixed Content Load initiated in nsMixedContentBlocker?
// * If not, the state should not be broken because no actual mixed-content
// load has occurred, overriding the previous state if it was flagged as mixed.
if (lock == lis_mixed_security &&
!docShell->GetHasMixedActiveContentLoaded() &&
!docShell->GetHasMixedDisplayContentLoaded() &&
!docShell->GetHasMixedActiveContentBlocked() &&
!docShell->GetHasMixedDisplayContentBlocked()) {
*aState = STATE_IS_SECURE | STATE_SECURE_HIGH;
if (ev) {
*aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
}
if (mCertUserOverridden) {
*aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
}
}
// * If so, the state should be broken or insecure; overriding the previous
// state set by the lock parameter.
uint32_t tempState = STATE_IS_BROKEN;
if (lock == lis_no_security) {
// this is to ensure that http: pages with mixed content in nested
// iframes don't get marked as broken instead of insecure
tempState = STATE_IS_INSECURE;
}
if (docShell->GetHasMixedActiveContentLoaded() &&
docShell->GetHasMixedDisplayContentLoaded()) {
*aState = tempState |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
} else if (docShell->GetHasMixedActiveContentLoaded()) {
*aState = tempState |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
} else if (docShell->GetHasMixedDisplayContentLoaded()) {
*aState = tempState |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
}
// Has Mixed Content Been Blocked in nsMixedContentBlocker?
if (docShell->GetHasMixedActiveContentBlocked())
*aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
if (docShell->GetHasMixedDisplayContentBlocked())
*aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
// Has Tracking Content been Blocked?
if (docShell->GetHasTrackingContentBlocked())
*aState |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
if (docShell->GetHasTrackingContentLoaded())
*aState |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
return NS_OK;
}
NS_IMETHODIMP
nsSecureBrowserUIImpl::SetDocShell(nsIDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
mDocShell = do_GetWeakReference(aDocShell, &rv);
return rv;
}
static uint32_t GetSecurityStateFromSecurityInfoAndRequest(nsISupports* info,
nsIRequest* request)
{
nsresult res;
uint32_t securityState;
nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
if (!psmInfo) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - no nsITransportSecurityInfo for %p\n",
(nsISupports *)info));
return nsIWebProgressListener::STATE_IS_INSECURE;
}
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - info is %p\n",
(nsISupports *)info));
res = psmInfo->GetSecurityState(&securityState);
if (NS_FAILED(res)) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - GetSecurityState failed: %d\n",
res));
securityState = nsIWebProgressListener::STATE_IS_BROKEN;
}
if (securityState != nsIWebProgressListener::STATE_IS_INSECURE) {
// A secure connection does not yield a secure per-uri channel if the
// scheme is plain http.
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
if (channel) {
channel->GetURI(getter_AddRefs(uri));
} else {
nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(request));
if (imgRequest) {
imgRequest->GetURI(getter_AddRefs(uri));
}
}
if (uri) {
bool isHttp, isFtp;
if ((NS_SUCCEEDED(uri->SchemeIs("http", &isHttp)) && isHttp) ||
(NS_SUCCEEDED(uri->SchemeIs("ftp", &isFtp)) && isFtp)) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - "
"channel scheme is insecure.\n"));
securityState = nsIWebProgressListener::STATE_IS_INSECURE;
}
}
}
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - Returning %d\n",
securityState));
return securityState;
}
// nsIWebProgressListener
NS_IMETHODIMP
nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
int32_t aCurSelfProgress,
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
int32_t aMaxTotalProgress)
{
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
return NS_OK;
}
void
nsSecureBrowserUIImpl::ResetStateTracking()
{
mDocumentRequestsInProgress = 0;
mTransferringRequests.Clear();
}
void
nsSecureBrowserUIImpl::EvaluateAndUpdateSecurityState(nsIRequest* aRequest,
nsISupports* info,
bool withNewLocation,
bool withNewSink)
{
mNewToplevelIsEV = false;
bool updateStatus = false;
nsCOMPtr<nsISSLStatus> temp_SSLStatus;
mNewToplevelSecurityState =
GetSecurityStateFromSecurityInfoAndRequest(info, aRequest);
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n",
this, mNewToplevelSecurityState));
nsCOMPtr<nsISSLStatusProvider> sp(do_QueryInterface(info));
if (sp) {
// Ignore result
updateStatus = true;
(void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
if (temp_SSLStatus) {
bool aTemp;
if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) {
mNewToplevelIsEV = aTemp;
}
}
}
mNewToplevelSecurityStateKnown = true;
if (updateStatus) {
mSSLStatus = temp_SSLStatus;
}
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: remember securityInfo %p\n", this,
info));
nsCOMPtr<nsIAssociatedContentSecurity> associatedContentSecurityFromRequest(
do_QueryInterface(aRequest));
if (associatedContentSecurityFromRequest) {
mCurrentToplevelSecurityInfo = aRequest;
} else {
mCurrentToplevelSecurityInfo = info;
}
// The subrequest counters are now in sync with mCurrentToplevelSecurityInfo,
// don't restore after top level document load finishes.
mRestoreSubrequests = false;
UpdateSecurityState(aRequest, withNewLocation, withNewSink || updateStatus);
}
void
nsSecureBrowserUIImpl::UpdateSubrequestMembers(nsISupports* securityInfo,
nsIRequest* request)
{
// For wyciwyg channels in subdocuments we only update our
// subrequest state members.
uint32_t reqState = GetSecurityStateFromSecurityInfoAndRequest(securityInfo,
request);
if (reqState & STATE_IS_SECURE) {
// do nothing
} else if (reqState & STATE_IS_BROKEN) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: subreq BROKEN\n", this));
++mSubRequestsBrokenSecurity;
} else {
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: subreq INSECURE\n", this));
++mSubRequestsNoSecurity;
}
}
NS_IMETHODIMP
nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aProgressStateFlags,
nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef DEBUG
nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
"unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
#endif
/*
All discussion, unless otherwise mentioned, only refers to
http, https, file or wyciwig requests.
Redirects are evil, well, some of them.
There are multiple forms of redirects.
Redirects caused by http refresh content are ok, because experiments show,
with those redirects, the old page contents and their requests will come to STOP
completely, before any progress from new refreshed page content is reported.
So we can safely treat them as separate page loading transactions.
Evil are redirects at the http protocol level, like code 302.
If the toplevel documents gets replaced, i.e. redirected with 302, we do not care for the
security state of the initial transaction, which has now been redirected,
we only care for the new page load.
For the implementation of the security UI, we make an assumption, that is hopefully true.
Imagine, the received page that was delivered with the 302 redirection answer,
also delivered html content.
What happens if the parser starts to analyze the content and tries to load contained sub objects?
In that case we would see start and stop requests for subdocuments, some for the previous document,
some for the new target document. And only those for the new toplevel document may be
taken into consideration, when deciding about the security state of the next toplevel document.
Because security state is being looked at, when loading stops for (sub)documents, this
could cause real confusion, because we have to decide, whether an incoming progress
belongs to the new toplevel page, or the previous, already redirected page.
Can we simplify here?
If a redirect at the http protocol level is seen, can we safely assume, its html content
will not be parsed, anylzed, and no embedded objects will get loaded (css, js, images),
because the redirect is already happening?
If we can assume that, this really simplify things. Because we will never see notification
for sub requests that need to get ignored.
I would like to make this assumption for now, but please let me (kaie) know if I'm wrong.
Excurse:
If my assumption is wrong, then we would require more tracking information.
We need to keep lists of all pointers to request object that had been seen since the
last toplevel start event.
If the start for a redirected page is seen, the list of releveant object must be cleared,
and only progress for requests which start after it must be analyzed.
All other events must be ignored, as they belong to now irrelevant previous top level documents.
Frames are also evil.
First we need a decision.
kaie thinks:
Only if the toplevel frame is secure, we should try to display secure lock icons.
If some of the inner contents are insecure, we display mixed mode.
But if the top level frame is not secure, why indicate a mixed lock icon at all?
I think we should always display an open lock icon, if the top level frameset is insecure.
That's the way Netscape Communicator behaves, and I think we should do the same.
The user will not know which parts are secure and which are not,
and any certificate information, displayed in the tooltip or in the "page info"
will only be relevant for some subframe(s), and the user will not know which ones,
so we shouldn't display it as a general attribute of the displayed page.
Why are frames evil?
Because the progress for the toplevel frame document is not easily distinguishable
from subframes. The same STATE bits are reported.
While at first sight, when a new page load happens,
the toplevel frameset document has also the STATE_IS_NETWORK bit in it.
But this can't really be used. Because in case that document causes a http 302 redirect,
the real top level frameset will no longer have that bit.
But we need some way to distinguish top level frames from inner frames.
I saw that the web progress we get delivered has a reference to the toplevel DOM window.
I suggest, we look at all incoming requests.
If a request is NOT for the toplevel DOM window, we will always treat it as a subdocument request,
regardless of whether the load flags indicate a top level document.
*/
nsCOMPtr<nsIDOMWindow> windowForProgress;
aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
NS_ASSERTION(window, "Window has gone away?!");
if (!mIOService) {
mIOService = do_GetService(NS_IOSERVICE_CONTRACTID);
}
bool isNoContentResponse = false;
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
if (httpChannel)
{
uint32_t response;
isNoContentResponse = NS_SUCCEEDED(httpChannel->GetResponseStatus(&response)) &&
(response == 204 || response == 205);
}
const bool isToplevelProgress = (windowForProgress.get() == window.get()) && !isNoContentResponse;
if (windowForProgress)
{
if (isToplevelProgress)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: progress: for toplevel\n", this));
}
else
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: progress: for something else\n", this));
}
}
else
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: progress: no window known\n", this));
}
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange\n", this));
if (mIsViewSource) {
return NS_OK;
}
if (!aRequest)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange with null request\n", this));
return NS_ERROR_NULL_POINTER;
}
if (MOZ_LOG_TEST(gSecureDocLog, LogLevel::Debug)) {
nsXPIDLCString reqname;
aRequest->GetName(reqname);
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: %p %p OnStateChange %x %s\n", this, aWebProgress,
aRequest, aProgressStateFlags, reqname.get()));
}
nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
if (channel) {
channel->GetURI(getter_AddRefs(uri));
}
nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(aRequest));
if (imgRequest) {
NS_ASSERTION(!channel, "How did that happen, exactly?");
// for image requests, we get the URI from here
imgRequest->GetURI(getter_AddRefs(uri));
}
if (uri) {
bool vs;
if (NS_SUCCEEDED(uri->SchemeIs("javascript", &vs)) && vs) {
// We ignore the progress events for javascript URLs.
// If a document loading gets triggered, we will see more events.
return NS_OK;
}
}
uint32_t loadFlags = 0;
aRequest->GetLoadFlags(&loadFlags);
if (aProgressStateFlags & STATE_START
&&
aProgressStateFlags & STATE_IS_REQUEST
&&
isToplevelProgress
&&
loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: SOMETHING STARTS FOR TOPMOST DOCUMENT\n", this));
}
if (aProgressStateFlags & STATE_STOP
&&
aProgressStateFlags & STATE_IS_REQUEST
&&
isToplevelProgress
&&
loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: SOMETHING STOPS FOR TOPMOST DOCUMENT\n", this));
}
bool isSubDocumentRelevant = true;
// We are only interested in requests that load in the browser window...
if (!imgRequest) { // is not imgRequest
nsCOMPtr<nsIHttpChannel> httpRequest(do_QueryInterface(aRequest));
if (!httpRequest) {
nsCOMPtr<nsIFileChannel> fileRequest(do_QueryInterface(aRequest));
if (!fileRequest) {
nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
if (!wyciwygRequest) {
nsCOMPtr<nsIFTPChannel> ftpRequest(do_QueryInterface(aRequest));
if (!ftpRequest) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: not relevant for sub content\n", this));
isSubDocumentRelevant = false;
}
}
}
}
}
// This will ignore all resource, chrome, data, file, moz-icon, and anno
// protocols. Local resources are treated as trusted.
if (uri && mIOService) {
bool hasFlag;
nsresult rv =
mIOService->URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
&hasFlag);
if (NS_SUCCEEDED(rv) && hasFlag) {
isSubDocumentRelevant = false;
}
}
#if defined(DEBUG)
if (aProgressStateFlags & STATE_STOP
&&
channel)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: seeing STOP with security state: %d\n", this,
GetSecurityStateFromSecurityInfoAndRequest(securityInfo, aRequest)
));
}
#endif
if (aProgressStateFlags & STATE_TRANSFERRING
&&
aProgressStateFlags & STATE_IS_REQUEST)
{
// The listing of a request in mTransferringRequests
// means, there has already been data transfered.
mTransferringRequests.Add(aRequest, fallible);
return NS_OK;
}
bool requestHasTransferedData = false;
if (aProgressStateFlags & STATE_STOP
&&
aProgressStateFlags & STATE_IS_REQUEST)
{
PLDHashEntryHdr* entry = mTransferringRequests.Search(aRequest);
if (entry) {
mTransferringRequests.RemoveEntry(entry);
requestHasTransferedData = true;
}
if (!requestHasTransferedData) {
// Because image loads doesn't support any TRANSFERRING notifications but
// only START and STOP we must ask them directly whether content was
// transferred. See bug 432685 for details.
nsCOMPtr<nsISecurityInfoProvider> securityInfoProvider =
do_QueryInterface(aRequest);
// Guess true in all failure cases to be safe. But if we're not
// an nsISecurityInfoProvider, then we just haven't transferred
// any data.
bool hasTransferred;
requestHasTransferedData =
securityInfoProvider &&
(NS_FAILED(securityInfoProvider->GetHasTransferredData(&hasTransferred)) ||
hasTransferred);
}
}
bool allowSecurityStateChange = true;
if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
{
// The original consumer (this) is no longer the target of the load.
// Ignore any events with this flag, do not allow them to update
// our secure UI state.
allowSecurityStateChange = false;
}
if (aProgressStateFlags & STATE_START
&&
aProgressStateFlags & STATE_IS_REQUEST
&&
isToplevelProgress
&&
loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
{
int32_t saveSubBroken;
int32_t saveSubNo;
nsCOMPtr<nsIAssociatedContentSecurity> prevContentSecurity;
int32_t newSubBroken = 0;
int32_t newSubNo = 0;
bool inProgress = (mDocumentRequestsInProgress != 0);
if (allowSecurityStateChange && !inProgress) {
saveSubBroken = mSubRequestsBrokenSecurity;
saveSubNo = mSubRequestsNoSecurity;
prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo);
}
if (allowSecurityStateChange && !inProgress)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: start for toplevel document\n", this
));
if (prevContentSecurity)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: start, saving current sub state\n", this
));
// before resetting our state, let's save information about
// sub element loads, so we can restore it later
prevContentSecurity->SetCountSubRequestsBrokenSecurity(saveSubBroken);
prevContentSecurity->SetCountSubRequestsNoSecurity(saveSubNo);
prevContentSecurity->Flush();
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: Saving subs in START to %p as %d,%d\n",
this, prevContentSecurity.get(), saveSubBroken, saveSubNo));
}
bool retrieveAssociatedState = false;
if (securityInfo &&
(aProgressStateFlags & nsIWebProgressListener::STATE_RESTORING) != 0) {
retrieveAssociatedState = true;
} else {
nsCOMPtr<nsIWyciwygChannel> wyciwygRequest(do_QueryInterface(aRequest));
if (wyciwygRequest) {
retrieveAssociatedState = true;
}
}
if (retrieveAssociatedState)
{
// When restoring from bfcache, we will not get events for the
// page's sub elements, so let's load the state of sub elements
// from the cache.
nsCOMPtr<nsIAssociatedContentSecurity>
newContentSecurity(do_QueryInterface(securityInfo));
if (newContentSecurity)
{
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: start, loading old sub state\n", this
));
newContentSecurity->GetCountSubRequestsBrokenSecurity(&newSubBroken);
newContentSecurity->GetCountSubRequestsNoSecurity(&newSubNo);
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: Restoring subs in START from %p to %d,%d\n",
this, newContentSecurity.get(), newSubBroken, newSubNo));
}
}
else
{
// If we don't get OnLocationChange for this top level load later,
// it didn't get rendered. But we reset the state to unknown and
// mSubRequests* to zeros. If we would have left these values after
// this top level load stoped, we would override the original top level
// load with all zeros and break mixed content state on back and forward.
mRestoreSubrequests = true;
}
}
if (allowSecurityStateChange && !inProgress) {
ResetStateTracking();
mSubRequestsBrokenSecurity = newSubBroken;
mSubRequestsNoSecurity = newSubNo;
mNewToplevelSecurityStateKnown = false;
}
// By using a counter, this code also works when the toplevel
// document get's redirected, but the STOP request for the
// previous toplevel document has not yet have been received.
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this
));
++mDocumentRequestsInProgress;
return NS_OK;
}
if (aProgressStateFlags & STATE_STOP
&&
aProgressStateFlags & STATE_IS_REQUEST
&&
isToplevelProgress
&&
loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
{
nsCOMPtr<nsISecurityEventSink> temp_ToplevelEventSink;
if (allowSecurityStateChange) {
temp_ToplevelEventSink = mToplevelEventSink;
}
if (mDocumentRequestsInProgress <= 0) {
// Ignore stop requests unless a document load is in progress
// Unfortunately on application start, see some stops without having seen any starts...
return NS_OK;
}
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnStateChange: --mDocumentRequestsInProgress\n", this
));
if (!temp_ToplevelEventSink && channel)
{
if (allowSecurityStateChange)
{
ObtainEventSink(channel, temp_ToplevelEventSink);
}
}
bool sinkChanged = false;
bool inProgress;
if (allowSecurityStateChange) {
sinkChanged = (mToplevelEventSink != temp_ToplevelEventSink);
mToplevelEventSink = temp_ToplevelEventSink;
}
--mDocumentRequestsInProgress;
inProgress = mDocumentRequestsInProgress > 0;
if (allowSecurityStateChange && requestHasTransferedData) {
// Data has been transferred for the single toplevel
// request. Evaluate the security state.
// Do this only when the sink has changed. We update and notify
// the state from OnLacationChange, this is actually redundant.
// But when the target sink changes between OnLocationChange and
// OnStateChange, we have to fire the notification here (again).
if (sinkChanged || mOnLocationChangeSeen) {
EvaluateAndUpdateSecurityState(aRequest, securityInfo, false,
sinkChanged);
return NS_OK;
}
}
mOnLocationChangeSeen = false;
if (mRestoreSubrequests && !inProgress)
{
// We get here when there were no OnLocationChange between
// OnStateChange(START) and OnStateChange(STOP). Then the load has not
// been rendered but has been retargeted in some other way then by external
// app handler. Restore mSubRequests* members to what the current security
// state info holds (it was reset to all zero in OnStateChange(START)
// before).
nsCOMPtr<nsIAssociatedContentSecurity> currentContentSecurity(
do_QueryInterface(mCurrentToplevelSecurityInfo));
// Drop this indication flag, the restore operation is just being done.
mRestoreSubrequests = false;
// We can do this since the state didn't actually change.
mNewToplevelSecurityStateKnown = true;
int32_t subBroken = 0;
int32_t subNo = 0;
if (currentContentSecurity)
{
currentContentSecurity->GetCountSubRequestsBrokenSecurity(&subBroken);
currentContentSecurity->GetCountSubRequestsNoSecurity(&subNo);
MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: Restoring subs in STOP from %p to %d,%d\n",
this, currentContentSecurity.get(), subBroken, subNo));
}
mSubRequestsBrokenSecurity = subBroken;
mSubRequestsNoSecurity = subNo;
}
return NS_OK;
}
if (aProgressStateFlags & STATE_STOP
&&
aProgressStateFlags & STATE_IS_REQUEST)
{
if (!isSubDocumentRelevant)
return NS_OK;
// if we arrive here, LOAD_DOCUMENT_URI is not set
// We only care for the security state of sub requests which have actually transfered data.
if (allowSecurityStateChange && requestHasTransferedData)
{
UpdateSubrequestMembers(securityInfo, aRequest);
// Care for the following scenario:
// A new top level document load might have already started,
// but the security state of the new top level document might not yet been known.
//
// At this point, we are learning about the security state of a sub-document.
// We must not update the security state based on the sub content,
// if the new top level state is not yet known.
//
// We skip updating the security state in this case.
if (mNewToplevelSecurityStateKnown) {
UpdateSecurityState(aRequest, false, false);
}
}
return NS_OK;
}
return NS_OK;
}
// I'm keeping this as a separate function, in order to simplify the review
// for bug 412456. We should inline this in a follow up patch.
void nsSecureBrowserUIImpl::ObtainEventSink(nsIChannel *channel,
nsCOMPtr<nsISecurityEventSink> &sink)
{
if (!sink)
NS_QueryNotificationCallbacks(channel, sink);
}
void
nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest,
bool withNewLocation,
bool withUpdateStatus)
{
lockIconState newSecurityState = lis_no_security;
if (mNewToplevelSecurityState & STATE_IS_SECURE) {
// If a subresoure/request was insecure, then we have mixed security.
if (mSubRequestsBrokenSecurity || mSubRequestsNoSecurity) {
newSecurityState = lis_mixed_security;
} else {
newSecurityState = lis_high_security;
}
}
if (mNewToplevelSecurityState & STATE_IS_BROKEN) {
newSecurityState = lis_broken_security;
}
mCertUserOverridden =
mNewToplevelSecurityState & STATE_CERT_USER_OVERRIDDEN;
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: UpdateSecurityState: old-new %d - %d\n", this,
mNotifiedSecurityState, newSecurityState));
bool flagsChanged = false;
if (mNotifiedSecurityState != newSecurityState) {
// Something changed since the last time.
flagsChanged = true;
mNotifiedSecurityState = newSecurityState;
// If we have no security, we also shouldn't have any SSL status.
if (newSecurityState == lis_no_security) {
mSSLStatus = nullptr;
}
}
if (mNotifiedToplevelIsEV != mNewToplevelIsEV) {
flagsChanged = true;
mNotifiedToplevelIsEV = mNewToplevelIsEV;
}
if (flagsChanged || withNewLocation || withUpdateStatus) {
TellTheWorld(aRequest);
}
}
void
nsSecureBrowserUIImpl::TellTheWorld(nsIRequest* aRequest)
{
uint32_t state = STATE_IS_INSECURE;
GetState(&state);
if (mToplevelEventSink) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: UpdateSecurityState: calling OnSecurityChange\n",
this));
mToplevelEventSink->OnSecurityChange(aRequest, state);
} else {
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: UpdateSecurityState: NO mToplevelEventSink!\n",
this));
}
}
NS_IMETHODIMP
nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsIURI* aLocation,
uint32_t aFlags)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef DEBUG
nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection);
NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1,
"unexpected parallel nsIWebProgress OnStateChange and/or OnLocationChange notification");
#endif
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnLocationChange\n", this));
bool updateIsViewSource = false;
bool temp_IsViewSource = false;
nsCOMPtr<nsIDOMWindow> window;
if (aLocation)
{
bool vs;
nsresult rv = aLocation->SchemeIs("view-source", &vs);
NS_ENSURE_SUCCESS(rv, rv);
if (vs) {
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnLocationChange: view-source\n", this));
}
updateIsViewSource = true;
temp_IsViewSource = vs;
}
if (updateIsViewSource) {
mIsViewSource = temp_IsViewSource;
}
mCurrentURI = aLocation;
window = do_QueryReferent(mWindow);
NS_ASSERTION(window, "Window has gone away?!");
// When |aRequest| is null, basically we don't trust that document. But if
// docshell insists that the document has not changed at all, we will reuse
// the previous security state, no matter what |aRequest| may be.
if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT)
return NS_OK;
// The location bar has changed, so we must update the security state. The
// only concern with doing this here is that a page may transition from being
// reported as completely secure to being reported as partially secure
// (mixed). This may be confusing for users, and it may bother users who
// like seeing security dialogs. However, it seems prudent given that page
// loading may never end in some edge cases (perhaps by a site with malicious
// intent).
nsCOMPtr<nsIDOMWindow> windowForProgress;
aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
nsCOMPtr<nsISupports> securityInfo(ExtractSecurityInfo(aRequest));
if (windowForProgress.get() == window.get()) {
// For toplevel channels, update the security state right away.
mOnLocationChangeSeen = true;
EvaluateAndUpdateSecurityState(aRequest, securityInfo, true, false);
return NS_OK;
}
// For channels in subdocuments we only update our subrequest state members.
UpdateSubrequestMembers(securityInfo, aRequest);
// Care for the following scenario:
// A new toplevel document load might have already started, but the security
// state of the new toplevel document might not yet be known.
//
// At this point, we are learning about the security state of a sub-document.
// We must not update the security state based on the sub content, if the new
// top level state is not yet known.
//
// We skip updating the security state in this case.
if (mNewToplevelSecurityStateKnown) {
UpdateSecurityState(aRequest, true, false);
}
return NS_OK;
}
NS_IMETHODIMP
nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsresult aStatus,
const char16_t* aMessage)
{
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
return NS_OK;
}
nsresult
nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t state)
{
MOZ_ASSERT(NS_IsMainThread());
#if defined(DEBUG)
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
if (!channel)
return NS_OK;
nsCOMPtr<nsIURI> aURI;
channel->GetURI(getter_AddRefs(aURI));
if (aURI) {
nsAutoCString temp;
aURI->GetSpec(temp);
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: OnSecurityChange: (%x) %s\n", this,
state, temp.get()));
}
#endif
return NS_OK;
}
// nsISSLStatusProvider methods
NS_IMETHODIMP
nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result)
{
NS_ENSURE_ARG_POINTER(_result);
MOZ_ASSERT(NS_IsMainThread());
switch (mNotifiedSecurityState)
{
case lis_broken_security:
case lis_mixed_security:
case lis_high_security:
break;
default:
MOZ_FALLTHROUGH_ASSERT("if this is reached you must add more entries to the switch");
case lis_no_security:
*_result = nullptr;
return NS_OK;
}
*_result = mSSLStatus;
NS_IF_ADDREF(*_result);
return NS_OK;
}