/src/mozilla-central/dom/base/PostMessageEvent.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "PostMessageEvent.h" |
8 | | |
9 | | #include "MessageEvent.h" |
10 | | #include "mozilla/dom/BlobBinding.h" |
11 | | #include "mozilla/dom/File.h" |
12 | | #include "mozilla/dom/FileList.h" |
13 | | #include "mozilla/dom/FileListBinding.h" |
14 | | #include "mozilla/dom/MessageEventBinding.h" |
15 | | #include "mozilla/dom/MessagePort.h" |
16 | | #include "mozilla/dom/MessagePortBinding.h" |
17 | | #include "mozilla/dom/PMessagePort.h" |
18 | | #include "mozilla/dom/StructuredCloneTags.h" |
19 | | #include "mozilla/dom/UnionConversions.h" |
20 | | #include "mozilla/EventDispatcher.h" |
21 | | #include "nsContentUtils.h" |
22 | | #include "nsGlobalWindow.h" |
23 | | #include "nsIPresShell.h" |
24 | | #include "nsIPrincipal.h" |
25 | | #include "nsIScriptError.h" |
26 | | #include "nsPresContext.h" |
27 | | #include "nsQueryObject.h" |
28 | | |
29 | | namespace mozilla { |
30 | | namespace dom { |
31 | | |
32 | | PostMessageEvent::PostMessageEvent(nsGlobalWindowOuter* aSource, |
33 | | const nsAString& aCallerOrigin, |
34 | | nsGlobalWindowOuter* aTargetWindow, |
35 | | nsIPrincipal* aProvidedPrincipal, |
36 | | nsIDocument* aSourceDocument, |
37 | | bool aTrustedCaller) |
38 | | : Runnable("dom::PostMessageEvent") |
39 | | , StructuredCloneHolder(CloningSupported, |
40 | | TransferringSupported, |
41 | | StructuredCloneScope::SameProcessSameThread) |
42 | | , mSource(aSource) |
43 | | , mCallerOrigin(aCallerOrigin) |
44 | | , mTargetWindow(aTargetWindow) |
45 | | , mProvidedPrincipal(aProvidedPrincipal) |
46 | | , mSourceDocument(aSourceDocument) |
47 | | , mTrustedCaller(aTrustedCaller) |
48 | 0 | { |
49 | 0 | } |
50 | | |
51 | | PostMessageEvent::~PostMessageEvent() |
52 | 0 | { |
53 | 0 | } |
54 | | |
55 | | NS_IMETHODIMP |
56 | | PostMessageEvent::Run() |
57 | 0 | { |
58 | 0 | // Note: We don't init this AutoJSAPI with targetWindow, because we do not |
59 | 0 | // want exceptions during message deserialization to trigger error events on |
60 | 0 | // targetWindow. |
61 | 0 | AutoJSAPI jsapi; |
62 | 0 | jsapi.Init(); |
63 | 0 | JSContext* cx = jsapi.cx(); |
64 | 0 |
|
65 | 0 | // The document is just used for the principal mismatch error message below. |
66 | 0 | // Use a stack variable so mSourceDocument is not held onto after this method |
67 | 0 | // finishes, regardless of the method outcome. |
68 | 0 | nsCOMPtr<nsIDocument> sourceDocument; |
69 | 0 | sourceDocument.swap(mSourceDocument); |
70 | 0 |
|
71 | 0 | // If we bailed before this point we're going to leak mMessage, but |
72 | 0 | // that's probably better than crashing. |
73 | 0 |
|
74 | 0 | RefPtr<nsGlobalWindowInner> targetWindow; |
75 | 0 | if (mTargetWindow->IsClosedOrClosing() || |
76 | 0 | !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) || |
77 | 0 | targetWindow->IsDying()) |
78 | 0 | return NS_OK; |
79 | 0 | |
80 | 0 | JSAutoRealm ar(cx, targetWindow->GetWrapper()); |
81 | 0 |
|
82 | 0 | // Ensure that any origin which might have been provided is the origin of this |
83 | 0 | // window's document. Note that we do this *now* instead of when postMessage |
84 | 0 | // is called because the target window might have been navigated to a |
85 | 0 | // different location between then and now. If this check happened when |
86 | 0 | // postMessage was called, it would be fairly easy for a malicious webpage to |
87 | 0 | // intercept messages intended for another site by carefully timing navigation |
88 | 0 | // of the target window so it changed location after postMessage but before |
89 | 0 | // now. |
90 | 0 | if (mProvidedPrincipal) { |
91 | 0 | // Get the target's origin either from its principal or, in the case the |
92 | 0 | // principal doesn't carry a URI (e.g. the system principal), the target's |
93 | 0 | // document. |
94 | 0 | nsIPrincipal* targetPrin = targetWindow->GetPrincipal(); |
95 | 0 | if (NS_WARN_IF(!targetPrin)) |
96 | 0 | return NS_OK; |
97 | 0 | |
98 | 0 | // Note: This is contrary to the spec with respect to file: URLs, which |
99 | 0 | // the spec groups into a single origin, but given we intentionally |
100 | 0 | // don't do that in other places it seems better to hold the line for |
101 | 0 | // now. Long-term, we want HTML5 to address this so that we can |
102 | 0 | // be compliant while being safer. |
103 | 0 | if (!targetPrin->Equals(mProvidedPrincipal)) { |
104 | 0 | OriginAttributes sourceAttrs = mProvidedPrincipal->OriginAttributesRef(); |
105 | 0 | OriginAttributes targetAttrs = targetPrin->OriginAttributesRef(); |
106 | 0 |
|
107 | 0 | MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mAppId == targetAttrs.mAppId, |
108 | 0 | "Target and source should have the same mAppId attribute."); |
109 | 0 | MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mUserContextId == targetAttrs.mUserContextId, |
110 | 0 | "Target and source should have the same userContextId attribute."); |
111 | 0 | MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mInIsolatedMozBrowser == targetAttrs.mInIsolatedMozBrowser, |
112 | 0 | "Target and source should have the same inIsolatedMozBrowser attribute."); |
113 | 0 |
|
114 | 0 | if (!nsContentUtils::IsSystemOrExpandedPrincipal(targetPrin) && |
115 | 0 | !nsContentUtils::IsSystemOrExpandedPrincipal(mProvidedPrincipal) && |
116 | 0 | !mTrustedCaller) { |
117 | 0 | MOZ_DIAGNOSTIC_ASSERT(sourceAttrs.mPrivateBrowsingId == targetAttrs.mPrivateBrowsingId, |
118 | 0 | "Target and source should have the same mPrivateBrowsingId attribute."); |
119 | 0 | } |
120 | 0 |
|
121 | 0 | nsAutoString providedOrigin, targetOrigin; |
122 | 0 | nsresult rv = nsContentUtils::GetUTFOrigin(targetPrin, targetOrigin); |
123 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
124 | 0 | rv = nsContentUtils::GetUTFOrigin(mProvidedPrincipal, providedOrigin); |
125 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
126 | 0 |
|
127 | 0 | const char16_t* params[] = { providedOrigin.get(), targetOrigin.get() }; |
128 | 0 |
|
129 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, |
130 | 0 | NS_LITERAL_CSTRING("DOM Window"), sourceDocument, |
131 | 0 | nsContentUtils::eDOM_PROPERTIES, |
132 | 0 | "TargetPrincipalDoesNotMatch", |
133 | 0 | params, ArrayLength(params)); |
134 | 0 |
|
135 | 0 | return NS_OK; |
136 | 0 | } |
137 | 0 | } |
138 | 0 |
|
139 | 0 | IgnoredErrorResult rv; |
140 | 0 | JS::Rooted<JS::Value> messageData(cx); |
141 | 0 | nsCOMPtr<mozilla::dom::EventTarget> eventTarget = do_QueryObject(targetWindow); |
142 | 0 |
|
143 | 0 | Read(targetWindow->AsInner(), cx, &messageData, rv); |
144 | 0 | if (NS_WARN_IF(rv.Failed())) { |
145 | 0 | DispatchError(cx, targetWindow, eventTarget); |
146 | 0 | return NS_OK; |
147 | 0 | } |
148 | 0 | |
149 | 0 | // Create the event |
150 | 0 | RefPtr<MessageEvent> event = new MessageEvent(eventTarget, nullptr, nullptr); |
151 | 0 |
|
152 | 0 |
|
153 | 0 | Nullable<WindowProxyOrMessagePortOrServiceWorker> source; |
154 | 0 | source.SetValue().SetAsWindowProxy() = mSource ? mSource->AsOuter() : nullptr; |
155 | 0 |
|
156 | 0 | Sequence<OwningNonNull<MessagePort>> ports; |
157 | 0 | if (!TakeTransferredPortsAsSequence(ports)) { |
158 | 0 | DispatchError(cx, targetWindow, eventTarget); |
159 | 0 | return NS_OK; |
160 | 0 | } |
161 | 0 | |
162 | 0 | event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"), |
163 | 0 | CanBubble::eNo, Cancelable::eNo, |
164 | 0 | messageData, mCallerOrigin, |
165 | 0 | EmptyString(), source, ports); |
166 | 0 |
|
167 | 0 | Dispatch(targetWindow, event); |
168 | 0 | return NS_OK; |
169 | 0 | } |
170 | | |
171 | | void |
172 | | PostMessageEvent::DispatchError(JSContext* aCx, nsGlobalWindowInner* aTargetWindow, |
173 | | mozilla::dom::EventTarget* aEventTarget) |
174 | 0 | { |
175 | 0 | RootedDictionary<MessageEventInit> init(aCx); |
176 | 0 | init.mBubbles = false; |
177 | 0 | init.mCancelable = false; |
178 | 0 | init.mOrigin = mCallerOrigin; |
179 | 0 |
|
180 | 0 | if (mSource) { |
181 | 0 | init.mSource.SetValue().SetAsWindowProxy() = mSource->AsOuter(); |
182 | 0 | } |
183 | 0 |
|
184 | 0 | RefPtr<Event> event = |
185 | 0 | MessageEvent::Constructor(aEventTarget, NS_LITERAL_STRING("messageerror"), |
186 | 0 | init); |
187 | 0 | Dispatch(aTargetWindow, event); |
188 | 0 | } |
189 | | |
190 | | void |
191 | | PostMessageEvent::Dispatch(nsGlobalWindowInner* aTargetWindow, Event* aEvent) |
192 | 0 | { |
193 | 0 | // We can't simply call dispatchEvent on the window because doing so ends |
194 | 0 | // up flipping the trusted bit on the event, and we don't want that to |
195 | 0 | // happen because then untrusted content can call postMessage on a chrome |
196 | 0 | // window if it can get a reference to it. |
197 | 0 |
|
198 | 0 | RefPtr<nsPresContext> presContext = |
199 | 0 | aTargetWindow->GetExtantDoc()->GetPresContext(); |
200 | 0 |
|
201 | 0 | aEvent->SetTrusted(mTrustedCaller); |
202 | 0 | WidgetEvent* internalEvent = aEvent->WidgetEventPtr(); |
203 | 0 |
|
204 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
205 | 0 | EventDispatcher::Dispatch(aTargetWindow->AsInner(), |
206 | 0 | presContext, |
207 | 0 | internalEvent, |
208 | 0 | aEvent, |
209 | 0 | &status); |
210 | 0 | } |
211 | | |
212 | | } // namespace dom |
213 | | } // namespace mozilla |