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 "InProcessBrowserChildMessageManager.h"
8 #include "nsContentUtils.h"
9 #include "nsDocShell.h"
10 #include "nsIInterfaceRequestorUtils.h"
11 #include "nsComponentManagerUtils.h"
12 #include "nsFrameLoader.h"
13 #include "nsFrameLoaderOwner.h"
14 #include "nsQueryObject.h"
15 #include "xpcpublic.h"
16 #include "nsIMozBrowserFrame.h"
17 #include "mozilla/EventDispatcher.h"
18 #include "mozilla/dom/ChromeMessageSender.h"
19 #include "mozilla/dom/Document.h"
20 #include "mozilla/dom/MessageManagerBinding.h"
21 #include "mozilla/dom/SameProcessMessageQueue.h"
22 #include "mozilla/dom/ScriptLoader.h"
23 #include "mozilla/dom/WindowProxyHolder.h"
24 #include "mozilla/dom/JSActorService.h"
25 #include "mozilla/HoldDropJSObjects.h"
26 
27 using namespace mozilla;
28 using namespace mozilla::dom;
29 using namespace mozilla::dom::ipc;
30 
31 /* static */
32 already_AddRefed<InProcessBrowserChildMessageManager>
Create(nsDocShell * aShell,nsIContent * aOwner,nsFrameMessageManager * aChrome)33 InProcessBrowserChildMessageManager::Create(nsDocShell* aShell,
34                                             nsIContent* aOwner,
35                                             nsFrameMessageManager* aChrome) {
36   RefPtr<InProcessBrowserChildMessageManager> mm =
37       new InProcessBrowserChildMessageManager(aShell, aOwner, aChrome);
38 
39   NS_ENSURE_TRUE(mm->Init(), nullptr);
40 
41   if (XRE_IsParentProcess()) {
42     RefPtr<JSActorService> wasvc = JSActorService::GetSingleton();
43     wasvc->RegisterChromeEventTarget(mm);
44   }
45 
46   return mm.forget();
47 }
48 
DoSendBlockingMessage(const nsAString & aMessage,StructuredCloneData & aData,nsTArray<StructuredCloneData> * aRetVal)49 bool InProcessBrowserChildMessageManager::DoSendBlockingMessage(
50     const nsAString& aMessage, StructuredCloneData& aData,
51     nsTArray<StructuredCloneData>* aRetVal) {
52   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
53   queue->Flush();
54 
55   if (mChromeMessageManager) {
56     RefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
57     RefPtr<nsFrameLoader> fl = GetFrameLoader();
58     mm->ReceiveMessage(mOwner, fl, aMessage, true, &aData, aRetVal,
59                        IgnoreErrors());
60   }
61   return true;
62 }
63 
64 class nsAsyncMessageToParent : public nsSameProcessAsyncMessageBase,
65                                public SameProcessMessageQueue::Runnable {
66  public:
nsAsyncMessageToParent(InProcessBrowserChildMessageManager * aBrowserChild)67   explicit nsAsyncMessageToParent(
68       InProcessBrowserChildMessageManager* aBrowserChild)
69       : nsSameProcessAsyncMessageBase(), mBrowserChild(aBrowserChild) {}
70 
HandleMessage()71   virtual nsresult HandleMessage() override {
72     RefPtr<nsFrameLoader> fl = mBrowserChild->GetFrameLoader();
73     ReceiveMessage(mBrowserChild->mOwner, fl,
74                    mBrowserChild->mChromeMessageManager);
75     return NS_OK;
76   }
77   RefPtr<InProcessBrowserChildMessageManager> mBrowserChild;
78 };
79 
DoSendAsyncMessage(const nsAString & aMessage,StructuredCloneData & aData)80 nsresult InProcessBrowserChildMessageManager::DoSendAsyncMessage(
81     const nsAString& aMessage, StructuredCloneData& aData) {
82   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
83   RefPtr<nsAsyncMessageToParent> ev = new nsAsyncMessageToParent(this);
84 
85   nsresult rv = ev->Init(aMessage, aData);
86   if (NS_FAILED(rv)) {
87     return rv;
88   }
89 
90   queue->Push(ev);
91   return NS_OK;
92 }
93 
InProcessBrowserChildMessageManager(nsDocShell * aShell,nsIContent * aOwner,nsFrameMessageManager * aChrome)94 InProcessBrowserChildMessageManager::InProcessBrowserChildMessageManager(
95     nsDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome)
96     : ContentFrameMessageManager(new nsFrameMessageManager(this)),
97       mDocShell(aShell),
98       mLoadingScript(false),
99       mPreventEventsEscaping(false),
100       mOwner(aOwner),
101       mChromeMessageManager(aChrome) {
102   mozilla::HoldJSObjects(this);
103 
104   // If owner corresponds to an <iframe mozbrowser>, we'll have to tweak our
105   // GetEventTargetParent implementation.
106   nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwner);
107   if (browserFrame) {
108     mIsBrowserFrame = browserFrame->GetReallyIsBrowser();
109   } else {
110     mIsBrowserFrame = false;
111   }
112 }
113 
~InProcessBrowserChildMessageManager()114 InProcessBrowserChildMessageManager::~InProcessBrowserChildMessageManager() {
115   if (XRE_IsParentProcess()) {
116     JSActorService::UnregisterChromeEventTarget(this);
117   }
118 
119   mozilla::DropJSObjects(this);
120 }
121 
122 // This method isn't automatically forwarded safely because it's notxpcom, so
123 // the IDL binding doesn't know what value to return.
MarkForCC()124 void InProcessBrowserChildMessageManager::MarkForCC() {
125   MarkScopesForCC();
126   MessageManagerGlobal::MarkForCC();
127 }
128 
129 NS_IMPL_CYCLE_COLLECTION_CLASS(InProcessBrowserChildMessageManager)
130 
131 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
132     InProcessBrowserChildMessageManager, DOMEventTargetHelper)
133   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
134   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
135 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
136 
137 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(
138     InProcessBrowserChildMessageManager, DOMEventTargetHelper)
139   tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
140 NS_IMPL_CYCLE_COLLECTION_TRACE_END
141 
142 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
143     InProcessBrowserChildMessageManager, DOMEventTargetHelper)
144   if (XRE_IsParentProcess()) {
145     JSActorService::UnregisterChromeEventTarget(tmp);
146   }
147 
148   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
149   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
150   tmp->nsMessageManagerScriptExecutor::Unlink();
151   NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
152 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
153 
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InProcessBrowserChildMessageManager)154 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InProcessBrowserChildMessageManager)
155   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
156   NS_INTERFACE_MAP_ENTRY(nsIInProcessContentFrameMessageManager)
157   NS_INTERFACE_MAP_ENTRY(ContentFrameMessageManager)
158   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
159 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
160 
161 NS_IMPL_ADDREF_INHERITED(InProcessBrowserChildMessageManager,
162                          DOMEventTargetHelper)
163 NS_IMPL_RELEASE_INHERITED(InProcessBrowserChildMessageManager,
164                           DOMEventTargetHelper)
165 
166 JSObject* InProcessBrowserChildMessageManager::WrapObject(
167     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
168   return ContentFrameMessageManager_Binding::Wrap(aCx, this, aGivenProto);
169 }
170 
CacheFrameLoader(nsFrameLoader * aFrameLoader)171 void InProcessBrowserChildMessageManager::CacheFrameLoader(
172     nsFrameLoader* aFrameLoader) {
173   mFrameLoader = aFrameLoader;
174 }
175 
GetContent(ErrorResult & aError)176 Nullable<WindowProxyHolder> InProcessBrowserChildMessageManager::GetContent(
177     ErrorResult& aError) {
178   if (!mDocShell) {
179     return nullptr;
180   }
181   return WindowProxyHolder(mDocShell->GetBrowsingContext());
182 }
183 
184 already_AddRefed<nsIEventTarget>
GetTabEventTarget()185 InProcessBrowserChildMessageManager::GetTabEventTarget() {
186   nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
187   return target.forget();
188 }
189 
FireUnloadEvent()190 void InProcessBrowserChildMessageManager::FireUnloadEvent() {
191   // We're called from Document::MaybeInitializeFinalizeFrameLoaders, so it
192   // should be safe to run script.
193   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
194 
195   // Don't let the unload event propagate to chrome event handlers.
196   mPreventEventsEscaping = true;
197   DOMEventTargetHelper::DispatchTrustedEvent(u"unload"_ns);
198 
199   // Allow events fired during docshell destruction (pagehide, unload) to
200   // propagate to the <browser> element since chrome code depends on this.
201   mPreventEventsEscaping = false;
202 }
203 
DisconnectEventListeners()204 void InProcessBrowserChildMessageManager::DisconnectEventListeners() {
205   if (mDocShell) {
206     if (nsCOMPtr<nsPIDOMWindowOuter> win = mDocShell->GetWindow()) {
207       win->SetChromeEventHandler(win->GetChromeEventHandler());
208     }
209   }
210   if (mListenerManager) {
211     mListenerManager->Disconnect();
212   }
213 
214   mDocShell = nullptr;
215 }
216 
Disconnect()217 void InProcessBrowserChildMessageManager::Disconnect() {
218   mChromeMessageManager = nullptr;
219   mOwner = nullptr;
220   if (mMessageManager) {
221     static_cast<nsFrameMessageManager*>(mMessageManager.get())->Disconnect();
222     mMessageManager = nullptr;
223   }
224 }
225 
NS_IMETHODIMP_(nsIContent *)226 NS_IMETHODIMP_(nsIContent*)
227 InProcessBrowserChildMessageManager::GetOwnerContent() { return mOwner; }
228 
GetEventTargetParent(EventChainPreVisitor & aVisitor)229 void InProcessBrowserChildMessageManager::GetEventTargetParent(
230     EventChainPreVisitor& aVisitor) {
231   aVisitor.mForceContentDispatch = true;
232   aVisitor.mCanHandle = true;
233 
234   if (mPreventEventsEscaping) {
235     aVisitor.SetParentTarget(nullptr, false);
236     return;
237   }
238 
239   if (mIsBrowserFrame &&
240       (!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) {
241     if (mOwner) {
242       if (nsPIDOMWindowInner* innerWindow =
243               mOwner->OwnerDoc()->GetInnerWindow()) {
244         // 'this' is already a "chrome handler", so we consider window's
245         // parent target to be part of that same part of the event path.
246         aVisitor.SetParentTarget(innerWindow->GetParentTarget(), false);
247       }
248     }
249   } else {
250     aVisitor.SetParentTarget(mOwner, false);
251   }
252 }
253 
254 class nsAsyncScriptLoad : public Runnable {
255  public:
nsAsyncScriptLoad(InProcessBrowserChildMessageManager * aBrowserChild,const nsAString & aURL,bool aRunInGlobalScope)256   nsAsyncScriptLoad(InProcessBrowserChildMessageManager* aBrowserChild,
257                     const nsAString& aURL, bool aRunInGlobalScope)
258       : mozilla::Runnable("nsAsyncScriptLoad"),
259         mBrowserChild(aBrowserChild),
260         mURL(aURL),
261         mRunInGlobalScope(aRunInGlobalScope) {}
262 
Run()263   NS_IMETHOD Run() override {
264     mBrowserChild->LoadFrameScript(mURL, mRunInGlobalScope);
265     return NS_OK;
266   }
267   RefPtr<InProcessBrowserChildMessageManager> mBrowserChild;
268   nsString mURL;
269   bool mRunInGlobalScope;
270 };
271 
LoadFrameScript(const nsAString & aURL,bool aRunInGlobalScope)272 void InProcessBrowserChildMessageManager::LoadFrameScript(
273     const nsAString& aURL, bool aRunInGlobalScope) {
274   if (!nsContentUtils::IsSafeToRunScript()) {
275     nsContentUtils::AddScriptRunner(
276         new nsAsyncScriptLoad(this, aURL, aRunInGlobalScope));
277     return;
278   }
279   bool tmp = mLoadingScript;
280   mLoadingScript = true;
281   JS::Rooted<JSObject*> mm(mozilla::dom::RootingCx(), GetOrCreateWrapper());
282   LoadScriptInternal(mm, aURL, !aRunInGlobalScope);
283   mLoadingScript = tmp;
284 }
285 
286 already_AddRefed<nsFrameLoader>
GetFrameLoader()287 InProcessBrowserChildMessageManager::GetFrameLoader() {
288   RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwner);
289   RefPtr<nsFrameLoader> fl = owner ? owner->GetFrameLoader() : nullptr;
290   if (!fl) {
291     fl = mFrameLoader;
292   }
293   return fl.forget();
294 }
295