Line data Source code
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 0 : PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
33 : const nsAString& aCallerOrigin,
34 : nsGlobalWindow* aTargetWindow,
35 : nsIPrincipal* aProvidedPrincipal,
36 : nsIDocument* aSourceDocument,
37 0 : 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 0 : , mTrustedCaller(aTrustedCaller)
48 : {
49 0 : }
50 :
51 0 : PostMessageEvent::~PostMessageEvent()
52 : {
53 0 : }
54 :
55 : NS_IMETHODIMP
56 0 : PostMessageEvent::Run()
57 : {
58 0 : MOZ_ASSERT(mTargetWindow->IsOuterWindow(),
59 : "should have been passed an outer window!");
60 0 : MOZ_ASSERT(!mSource || mSource->IsOuterWindow(),
61 : "should have been passed an outer window!");
62 :
63 : // Note: We don't init this AutoJSAPI with targetWindow, because we do not
64 : // want exceptions during message deserialization to trigger error events on
65 : // targetWindow.
66 0 : AutoJSAPI jsapi;
67 0 : jsapi.Init();
68 0 : JSContext* cx = jsapi.cx();
69 :
70 : // The document is just used for the principal mismatch error message below.
71 : // Use a stack variable so mSourceDocument is not held onto after this method
72 : // finishes, regardless of the method outcome.
73 0 : nsCOMPtr<nsIDocument> sourceDocument;
74 0 : sourceDocument.swap(mSourceDocument);
75 :
76 : // If we bailed before this point we're going to leak mMessage, but
77 : // that's probably better than crashing.
78 :
79 0 : RefPtr<nsGlobalWindow> targetWindow;
80 0 : if (mTargetWindow->IsClosedOrClosing() ||
81 0 : !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
82 0 : targetWindow->IsClosedOrClosing())
83 0 : return NS_OK;
84 :
85 0 : MOZ_ASSERT(targetWindow->IsInnerWindow(),
86 : "we ordered an inner window!");
87 0 : JSAutoCompartment ac(cx, targetWindow->GetWrapper());
88 :
89 : // Ensure that any origin which might have been provided is the origin of this
90 : // window's document. Note that we do this *now* instead of when postMessage
91 : // is called because the target window might have been navigated to a
92 : // different location between then and now. If this check happened when
93 : // postMessage was called, it would be fairly easy for a malicious webpage to
94 : // intercept messages intended for another site by carefully timing navigation
95 : // of the target window so it changed location after postMessage but before
96 : // now.
97 0 : if (mProvidedPrincipal) {
98 : // Get the target's origin either from its principal or, in the case the
99 : // principal doesn't carry a URI (e.g. the system principal), the target's
100 : // document.
101 0 : nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
102 0 : if (NS_WARN_IF(!targetPrin))
103 0 : return NS_OK;
104 :
105 : // Note: This is contrary to the spec with respect to file: URLs, which
106 : // the spec groups into a single origin, but given we intentionally
107 : // don't do that in other places it seems better to hold the line for
108 : // now. Long-term, we want HTML5 to address this so that we can
109 : // be compliant while being safer.
110 0 : if (!targetPrin->Equals(mProvidedPrincipal)) {
111 0 : MOZ_DIAGNOSTIC_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringFPD(mProvidedPrincipal->OriginAttributesRef(),
112 : targetPrin->OriginAttributesRef()),
113 : "Unexpected postMessage call to a window with mismatched "
114 : "origin attributes");
115 :
116 0 : nsAutoString providedOrigin, targetOrigin;
117 0 : nsresult rv = nsContentUtils::GetUTFOrigin(targetPrin, targetOrigin);
118 0 : NS_ENSURE_SUCCESS(rv, rv);
119 0 : rv = nsContentUtils::GetUTFOrigin(mProvidedPrincipal, providedOrigin);
120 0 : NS_ENSURE_SUCCESS(rv, rv);
121 :
122 0 : const char16_t* params[] = { providedOrigin.get(), targetOrigin.get() };
123 :
124 0 : nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
125 0 : NS_LITERAL_CSTRING("DOM Window"), sourceDocument,
126 : nsContentUtils::eDOM_PROPERTIES,
127 : "TargetPrincipalDoesNotMatch",
128 0 : params, ArrayLength(params));
129 :
130 0 : return NS_OK;
131 : }
132 : }
133 :
134 0 : ErrorResult rv;
135 0 : JS::Rooted<JS::Value> messageData(cx);
136 0 : nsCOMPtr<nsPIDOMWindowInner> window = targetWindow->AsInner();
137 :
138 0 : Read(window, cx, &messageData, rv);
139 0 : if (NS_WARN_IF(rv.Failed())) {
140 0 : return rv.StealNSResult();
141 : }
142 :
143 : // Create the event
144 0 : nsCOMPtr<mozilla::dom::EventTarget> eventTarget = do_QueryObject(targetWindow);
145 : RefPtr<MessageEvent> event =
146 0 : new MessageEvent(eventTarget, nullptr, nullptr);
147 :
148 :
149 0 : Nullable<WindowProxyOrMessagePortOrServiceWorker> source;
150 0 : source.SetValue().SetAsWindowProxy() = mSource ? mSource->AsOuter() : nullptr;
151 :
152 0 : Sequence<OwningNonNull<MessagePort>> ports;
153 0 : if (!TakeTransferredPortsAsSequence(ports)) {
154 0 : return NS_ERROR_OUT_OF_MEMORY;
155 : }
156 :
157 0 : event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"),
158 : false /*non-bubbling */, false /*cancelable */,
159 : messageData, mCallerOrigin,
160 0 : EmptyString(), source, ports);
161 :
162 : // We can't simply call dispatchEvent on the window because doing so ends
163 : // up flipping the trusted bit on the event, and we don't want that to
164 : // happen because then untrusted content can call postMessage on a chrome
165 : // window if it can get a reference to it.
166 :
167 0 : nsIPresShell *shell = targetWindow->GetExtantDoc()->GetShell();
168 0 : RefPtr<nsPresContext> presContext;
169 0 : if (shell)
170 0 : presContext = shell->GetPresContext();
171 :
172 0 : event->SetTrusted(mTrustedCaller);
173 0 : WidgetEvent* internalEvent = event->WidgetEventPtr();
174 :
175 0 : nsEventStatus status = nsEventStatus_eIgnore;
176 0 : EventDispatcher::Dispatch(window,
177 : presContext,
178 : internalEvent,
179 0 : static_cast<dom::Event*>(event.get()),
180 0 : &status);
181 0 : return NS_OK;
182 : }
183 :
184 : } // namespace dom
185 : } // namespace mozilla
|