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 "nsSecureBrowserUI.h"
7
8 #include "mozilla/Assertions.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/Unused.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsContentUtils.h"
13 #include "nsIChannel.h"
14 #include "nsDocShell.h"
15 #include "nsIDocShellTreeItem.h"
16 #include "nsGlobalWindow.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsITransportSecurityInfo.h"
19 #include "nsIWebProgress.h"
20 #include "nsNetUtil.h"
21 #include "mozilla/dom/CanonicalBrowsingContext.h"
22 #include "mozilla/dom/WindowGlobalParent.h"
23 #include "mozilla/dom/Element.h"
24 #include "nsIBrowser.h"
25
26 using namespace mozilla;
27 using namespace mozilla::dom;
28
29 LazyLogModule gSecureBrowserUILog("nsSecureBrowserUI");
30
nsSecureBrowserUI(CanonicalBrowsingContext * aBrowsingContext)31 nsSecureBrowserUI::nsSecureBrowserUI(CanonicalBrowsingContext* aBrowsingContext)
32 : mState(0) {
33 MOZ_ASSERT(NS_IsMainThread());
34
35 // The BrowsingContext will own the SecureBrowserUI object, we keep a weak
36 // ref.
37 mBrowsingContextId = aBrowsingContext->Id();
38 }
39
NS_IMPL_ISUPPORTS(nsSecureBrowserUI,nsISecureBrowserUI,nsISupportsWeakReference)40 NS_IMPL_ISUPPORTS(nsSecureBrowserUI, nsISecureBrowserUI,
41 nsISupportsWeakReference)
42
43 NS_IMETHODIMP
44 nsSecureBrowserUI::GetState(uint32_t* aState) {
45 MOZ_ASSERT(NS_IsMainThread());
46 NS_ENSURE_ARG(aState);
47
48 MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
49 ("GetState %p mState: %x", this, mState));
50 *aState = mState;
51 return NS_OK;
52 }
53
GetWebProgressListener(CanonicalBrowsingContext * aBrowsingContext,nsIBrowser ** aOutBrowser,nsIWebProgress ** aOutManager,nsIWebProgressListener ** aOutListener)54 static bool GetWebProgressListener(CanonicalBrowsingContext* aBrowsingContext,
55 nsIBrowser** aOutBrowser,
56 nsIWebProgress** aOutManager,
57 nsIWebProgressListener** aOutListener) {
58 MOZ_ASSERT(aOutBrowser);
59 MOZ_ASSERT(aOutManager);
60 MOZ_ASSERT(aOutListener);
61
62 nsCOMPtr<nsIBrowser> browser;
63 RefPtr<Element> currentElement = aBrowsingContext->GetEmbedderElement();
64
65 // In Responsive Design Mode, mFrameElement will be the <iframe mozbrowser>,
66 // but we want the <xul:browser> that it is embedded in.
67 while (currentElement) {
68 browser = currentElement->AsBrowser();
69 if (browser) {
70 break;
71 }
72
73 BrowsingContext* browsingContext =
74 currentElement->OwnerDoc()->GetBrowsingContext();
75 currentElement =
76 browsingContext ? browsingContext->GetEmbedderElement() : nullptr;
77 }
78
79 if (!browser) {
80 return false;
81 }
82
83 nsCOMPtr<nsIWebProgress> manager;
84 nsresult rv = browser->GetRemoteWebProgressManager(getter_AddRefs(manager));
85 if (NS_FAILED(rv)) {
86 browser.forget(aOutBrowser);
87 return true;
88 }
89
90 nsCOMPtr<nsIWebProgressListener> listener = do_QueryInterface(manager);
91 if (!listener) {
92 // We are no longer remote so we cannot forward this event.
93 browser.forget(aOutBrowser);
94 manager.forget(aOutManager);
95 return true;
96 }
97
98 browser.forget(aOutBrowser);
99 manager.forget(aOutManager);
100 listener.forget(aOutListener);
101
102 return true;
103 }
104
UpdateForLocationOrMixedContentChange()105 void nsSecureBrowserUI::UpdateForLocationOrMixedContentChange() {
106 // Our BrowsingContext either has a new WindowGlobalParent, or the
107 // existing one has mutated its security state.
108 // Recompute our security state and fire notifications to listeners
109
110 RefPtr<WindowGlobalParent> win = GetCurrentWindow();
111 mState = nsIWebProgressListener::STATE_IS_INSECURE;
112
113 // Only https is considered secure (it is possible to have e.g. an http URI
114 // with a channel that has a securityInfo that indicates the connection is
115 // secure - e.g. h2/alt-svc or by visiting an http URI over an https proxy).
116 nsCOMPtr<nsITransportSecurityInfo> securityInfo;
117 if (win && win->GetIsSecure()) {
118 securityInfo = win->GetSecurityInfo();
119 if (securityInfo) {
120 MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
121 (" we have a security info %p", securityInfo.get()));
122
123 nsresult rv = securityInfo->GetSecurityState(&mState);
124
125 // If the security state is STATE_IS_INSECURE, the TLS handshake never
126 // completed. Don't set any further state.
127 if (NS_SUCCEEDED(rv) &&
128 mState != nsIWebProgressListener::STATE_IS_INSECURE) {
129 MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
130 (" set mTopLevelSecurityInfo"));
131 bool isEV;
132 rv = securityInfo->GetIsExtendedValidation(&isEV);
133 if (NS_SUCCEEDED(rv) && isEV) {
134 MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, (" is EV"));
135 mState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
136 }
137 }
138 }
139 }
140
141 // Add the mixed content flags from the window
142 if (win) {
143 mState |= win->GetMixedContentSecurityFlags();
144 }
145
146 // If we have loaded mixed content and this is a secure page,
147 // then clear secure flags and add broken instead.
148 static const uint32_t kLoadedMixedContentFlags =
149 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
150 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
151 if (win && win->GetIsSecure() && (mState & kLoadedMixedContentFlags)) {
152 // reset state security flag
153 mState = mState >> 4 << 4;
154 // set state security flag to broken, since there is mixed content
155 mState |= nsIWebProgressListener::STATE_IS_BROKEN;
156 }
157
158 RefPtr<CanonicalBrowsingContext> ctx =
159 CanonicalBrowsingContext::Get(mBrowsingContextId);
160 if (!ctx) {
161 return;
162 }
163
164 // This is a bit painful, as we need to do different things for
165 // in-process docshells vs OOP ones.
166 // Ideally we'd just call a function on 'browser' which would
167 // handle sending an event to all listeners, and we wouldn't
168 // need to bother with onSecurityChange.
169 nsCOMPtr<nsIBrowser> browser;
170 nsCOMPtr<nsIWebProgress> manager;
171 nsCOMPtr<nsIWebProgressListener> managerAsListener;
172 if (!GetWebProgressListener(ctx, getter_AddRefs(browser),
173 getter_AddRefs(manager),
174 getter_AddRefs(managerAsListener))) {
175 return;
176 }
177
178 // Do we need to construct a fake webprogress and request instance?
179 // Should we split this API out of nsIWebProgressListener to avoid
180 // that?
181 if (managerAsListener) {
182 Unused << managerAsListener->OnSecurityChange(nullptr, nullptr, mState);
183 }
184 if (ctx->GetDocShell()) {
185 nsDocShell* nativeDocShell = nsDocShell::Cast(ctx->GetDocShell());
186 nativeDocShell->nsDocLoader::OnSecurityChange(nullptr, mState);
187 }
188 }
189
190 NS_IMETHODIMP
GetIsSecureContext(bool * aIsSecureContext)191 nsSecureBrowserUI::GetIsSecureContext(bool* aIsSecureContext) {
192 MOZ_ASSERT(NS_IsMainThread());
193 NS_ENSURE_ARG(aIsSecureContext);
194
195 if (WindowGlobalParent* parent = GetCurrentWindow()) {
196 *aIsSecureContext = parent->GetIsSecureContext();
197 } else {
198 *aIsSecureContext = false;
199 }
200 return NS_OK;
201 }
202
203 NS_IMETHODIMP
GetSecInfo(nsITransportSecurityInfo ** result)204 nsSecureBrowserUI::GetSecInfo(nsITransportSecurityInfo** result) {
205 MOZ_ASSERT(NS_IsMainThread());
206 NS_ENSURE_ARG_POINTER(result);
207
208 if (WindowGlobalParent* parent = GetCurrentWindow()) {
209 *result = parent->GetSecurityInfo();
210 }
211 NS_IF_ADDREF(*result);
212
213 return NS_OK;
214 }
215
GetCurrentWindow()216 WindowGlobalParent* nsSecureBrowserUI::GetCurrentWindow() {
217 RefPtr<CanonicalBrowsingContext> ctx =
218 CanonicalBrowsingContext::Get(mBrowsingContextId);
219 if (!ctx) {
220 return nullptr;
221 }
222 return ctx->GetCurrentWindowGlobal();
223 }
224