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 "mozilla/dom/TabContext.h"
8 #include "mozilla/dom/PTabContext.h"
9 #include "mozilla/dom/TabParent.h"
10 #include "mozilla/dom/TabChild.h"
11 #include "nsIAppsService.h"
12 #include "nsIScriptSecurityManager.h"
13 #include "nsServiceManagerUtils.h"
14 
15 #define NO_APP_ID (nsIScriptSecurityManager::NO_APP_ID)
16 
17 using namespace mozilla::dom::ipc;
18 using namespace mozilla::layout;
19 
20 namespace mozilla {
21 namespace dom {
22 
TabContext()23 TabContext::TabContext()
24   : mIsPrerendered(false)
25   , mInitialized(false)
26   , mIsMozBrowserElement(false)
27   , mContainingAppId(NO_APP_ID)
28   , mShowAccelerators(UIStateChangeType_NoChange)
29   , mShowFocusRings(UIStateChangeType_NoChange)
30 {
31 }
32 
33 bool
IsMozBrowserElement() const34 TabContext::IsMozBrowserElement() const
35 {
36   return mIsMozBrowserElement;
37 }
38 
39 bool
IsIsolatedMozBrowserElement() const40 TabContext::IsIsolatedMozBrowserElement() const
41 {
42   return mOriginAttributes.mInIsolatedMozBrowser;
43 }
44 
45 bool
IsMozBrowserOrApp() const46 TabContext::IsMozBrowserOrApp() const
47 {
48   return HasOwnApp() || IsMozBrowserElement();
49 }
50 
51 uint32_t
OwnAppId() const52 TabContext::OwnAppId() const
53 {
54   return mOriginAttributes.mAppId;
55 }
56 
57 already_AddRefed<mozIApplication>
GetOwnApp() const58 TabContext::GetOwnApp() const
59 {
60   nsCOMPtr<mozIApplication> ownApp = mOwnApp;
61   return ownApp.forget();
62 }
63 
64 bool
HasOwnApp() const65 TabContext::HasOwnApp() const
66 {
67   nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
68   return !!ownApp;
69 }
70 
71 uint32_t
BrowserOwnerAppId() const72 TabContext::BrowserOwnerAppId() const
73 {
74   if (IsMozBrowserElement()) {
75     return mContainingAppId;
76   }
77   return NO_APP_ID;
78 }
79 
80 already_AddRefed<mozIApplication>
GetBrowserOwnerApp() const81 TabContext::GetBrowserOwnerApp() const
82 {
83   nsCOMPtr<mozIApplication> ownerApp;
84   if (IsMozBrowserElement()) {
85     ownerApp = mContainingApp;
86   }
87   return ownerApp.forget();
88 }
89 
90 bool
HasBrowserOwnerApp() const91 TabContext::HasBrowserOwnerApp() const
92 {
93   nsCOMPtr<mozIApplication> ownerApp = GetBrowserOwnerApp();
94   return !!ownerApp;
95 }
96 
97 uint32_t
AppOwnerAppId() const98 TabContext::AppOwnerAppId() const
99 {
100   if (HasOwnApp()) {
101     return mContainingAppId;
102   }
103   return NO_APP_ID;
104 }
105 
106 already_AddRefed<mozIApplication>
GetAppOwnerApp() const107 TabContext::GetAppOwnerApp() const
108 {
109   nsCOMPtr<mozIApplication> ownerApp;
110   if (HasOwnApp()) {
111     ownerApp = mContainingApp;
112   }
113   return ownerApp.forget();
114 }
115 
116 bool
HasAppOwnerApp() const117 TabContext::HasAppOwnerApp() const
118 {
119   nsCOMPtr<mozIApplication> ownerApp = GetAppOwnerApp();
120   return !!ownerApp;
121 }
122 
123 uint32_t
OwnOrContainingAppId() const124 TabContext::OwnOrContainingAppId() const
125 {
126   if (HasOwnApp()) {
127     return mOriginAttributes.mAppId;
128   }
129 
130   return mContainingAppId;
131 }
132 
133 already_AddRefed<mozIApplication>
GetOwnOrContainingApp() const134 TabContext::GetOwnOrContainingApp() const
135 {
136   nsCOMPtr<mozIApplication> ownOrContainingApp;
137   if (HasOwnApp()) {
138     ownOrContainingApp = mOwnApp;
139   } else {
140     ownOrContainingApp = mContainingApp;
141   }
142 
143   return ownOrContainingApp.forget();
144 }
145 
146 bool
HasOwnOrContainingApp() const147 TabContext::HasOwnOrContainingApp() const
148 {
149   nsCOMPtr<mozIApplication> ownOrContainingApp = GetOwnOrContainingApp();
150   return !!ownOrContainingApp;
151 }
152 
153 bool
SetTabContext(const TabContext & aContext)154 TabContext::SetTabContext(const TabContext& aContext)
155 {
156   NS_ENSURE_FALSE(mInitialized, false);
157 
158   *this = aContext;
159   mInitialized = true;
160 
161   return true;
162 }
163 
164 void
SetPrivateBrowsingAttributes(bool aIsPrivateBrowsing)165 TabContext::SetPrivateBrowsingAttributes(bool aIsPrivateBrowsing)
166 {
167   mOriginAttributes.SyncAttributesWithPrivateBrowsing(aIsPrivateBrowsing);
168 }
169 
170 bool
UpdateTabContextAfterSwap(const TabContext & aContext)171 TabContext::UpdateTabContextAfterSwap(const TabContext& aContext)
172 {
173   // This is only used after already initialized.
174   MOZ_ASSERT(mInitialized);
175 
176   // The only permissable change is to `mIsMozBrowserElement`.  All other fields
177   // must match for the change to be accepted.
178   if (aContext.OwnAppId() != OwnAppId() ||
179       aContext.mContainingAppId != mContainingAppId ||
180       aContext.mOriginAttributes != mOriginAttributes) {
181     return false;
182   }
183 
184   mIsMozBrowserElement = aContext.mIsMozBrowserElement;
185   return true;
186 }
187 
188 const DocShellOriginAttributes&
OriginAttributesRef() const189 TabContext::OriginAttributesRef() const
190 {
191   return mOriginAttributes;
192 }
193 
194 const nsAString&
PresentationURL() const195 TabContext::PresentationURL() const
196 {
197   return mPresentationURL;
198 }
199 
200 UIStateChangeType
ShowAccelerators() const201 TabContext::ShowAccelerators() const
202 {
203   return mShowAccelerators;
204 }
205 
206 UIStateChangeType
ShowFocusRings() const207 TabContext::ShowFocusRings() const
208 {
209   return mShowFocusRings;
210 }
211 
212 bool
SetTabContext(bool aIsMozBrowserElement,bool aIsPrerendered,mozIApplication * aOwnApp,mozIApplication * aAppFrameOwnerApp,UIStateChangeType aShowAccelerators,UIStateChangeType aShowFocusRings,const DocShellOriginAttributes & aOriginAttributes,const nsAString & aPresentationURL)213 TabContext::SetTabContext(bool aIsMozBrowserElement,
214                           bool aIsPrerendered,
215                           mozIApplication* aOwnApp,
216                           mozIApplication* aAppFrameOwnerApp,
217                           UIStateChangeType aShowAccelerators,
218                           UIStateChangeType aShowFocusRings,
219                           const DocShellOriginAttributes& aOriginAttributes,
220                           const nsAString& aPresentationURL)
221 {
222   NS_ENSURE_FALSE(mInitialized, false);
223 
224   // Get ids for both apps and only write to our member variables after we've
225   // verified that this worked.
226   uint32_t ownAppId = NO_APP_ID;
227   if (aOwnApp) {
228     nsresult rv = aOwnApp->GetLocalId(&ownAppId);
229     NS_ENSURE_SUCCESS(rv, false);
230     NS_ENSURE_TRUE(ownAppId != NO_APP_ID, false);
231   }
232 
233   uint32_t containingAppId = NO_APP_ID;
234   if (aAppFrameOwnerApp) {
235     nsresult rv = aAppFrameOwnerApp->GetLocalId(&containingAppId);
236     NS_ENSURE_SUCCESS(rv, false);
237     NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false);
238   }
239 
240   // Veryify that app id matches mAppId passed in originAttributes
241   MOZ_RELEASE_ASSERT((aOwnApp && aOriginAttributes.mAppId == ownAppId) ||
242                      (aAppFrameOwnerApp && aOriginAttributes.mAppId == containingAppId) ||
243                      aOriginAttributes.mAppId == NO_APP_ID);
244 
245   mInitialized = true;
246   mIsMozBrowserElement = aIsMozBrowserElement;
247   mIsPrerendered = aIsPrerendered;
248   mOriginAttributes = aOriginAttributes;
249   mContainingAppId = containingAppId;
250   mOwnApp = aOwnApp;
251   mContainingApp = aAppFrameOwnerApp;
252   mPresentationURL = aPresentationURL;
253   mShowAccelerators = aShowAccelerators;
254   mShowFocusRings = aShowFocusRings;
255   return true;
256 }
257 
258 IPCTabContext
AsIPCTabContext() const259 TabContext::AsIPCTabContext() const
260 {
261   return IPCTabContext(FrameIPCTabContext(mOriginAttributes,
262                                           mContainingAppId,
263                                           mIsMozBrowserElement,
264                                           mIsPrerendered,
265                                           mPresentationURL,
266                                           mShowAccelerators,
267                                           mShowFocusRings));
268 }
269 
270 static already_AddRefed<mozIApplication>
GetAppForId(uint32_t aAppId)271 GetAppForId(uint32_t aAppId)
272 {
273   nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
274   NS_ENSURE_TRUE(appsService, nullptr);
275 
276   nsCOMPtr<mozIApplication> app;
277   appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
278 
279   return app.forget();
280 }
281 
MaybeInvalidTabContext(const IPCTabContext & aParams)282 MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
283   : mInvalidReason(nullptr)
284 {
285   bool isMozBrowserElement = false;
286   bool isPrerendered = false;
287   uint32_t containingAppId = NO_APP_ID;
288   DocShellOriginAttributes originAttributes;
289   nsAutoString presentationURL;
290   UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
291   UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
292 
293   switch(aParams.type()) {
294     case IPCTabContext::TPopupIPCTabContext: {
295       const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
296 
297       TabContext *context;
298       if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
299         context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent());
300         if (!context) {
301           mInvalidReason = "Child is-browser process tried to "
302                            "open a null tab.";
303           return;
304         }
305         if (context->IsMozBrowserElement() &&
306             !ipcContext.isMozBrowserElement()) {
307           // If the TabParent corresponds to a browser element, then it can only
308           // open other browser elements, for security reasons.  We should have
309           // checked this before calling the TabContext constructor, so this is
310           // a fatal error.
311           mInvalidReason = "Child is-browser process tried to "
312                            "open a non-browser tab.";
313           return;
314         }
315       } else if (ipcContext.opener().type() == PBrowserOrId::TPBrowserChild) {
316         context = static_cast<TabChild*>(ipcContext.opener().get_PBrowserChild());
317       } else if (ipcContext.opener().type() == PBrowserOrId::TTabId) {
318         // We should never get here because this PopupIPCTabContext is only
319         // used for allocating a new tab id, not for allocating a PBrowser.
320         mInvalidReason = "Child process tried to open an tab without the opener information.";
321         return;
322       } else {
323         // This should be unreachable because PopupIPCTabContext::opener is not a
324         // nullable field.
325         mInvalidReason = "PopupIPCTabContext::opener was null (?!).";
326         return;
327       }
328 
329       // Browser elements can't nest other browser elements.  So if
330       // our opener is browser element, we must be a new DOM window
331       // opened by it.  In that case we inherit our containing app ID
332       // (if any).
333       //
334       // Otherwise, we're a new app window and we inherit from our
335       // opener app.
336       isMozBrowserElement = ipcContext.isMozBrowserElement();
337       originAttributes = context->mOriginAttributes;
338       if (isMozBrowserElement) {
339         containingAppId = context->OwnOrContainingAppId();
340       } else {
341         containingAppId = context->mContainingAppId;
342       }
343       break;
344     }
345     case IPCTabContext::TFrameIPCTabContext: {
346       const FrameIPCTabContext &ipcContext =
347         aParams.get_FrameIPCTabContext();
348 
349       isMozBrowserElement = ipcContext.isMozBrowserElement();
350       isPrerendered = ipcContext.isPrerendered();
351       containingAppId = ipcContext.frameOwnerAppId();
352       presentationURL = ipcContext.presentationURL();
353       showAccelerators = ipcContext.showAccelerators();
354       showFocusRings = ipcContext.showFocusRings();
355       originAttributes = ipcContext.originAttributes();
356       break;
357     }
358     case IPCTabContext::TUnsafeIPCTabContext: {
359       // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow.
360       // It is meant as a temporary solution until service workers can
361       // provide a TabChild equivalent. Don't allow this on b2g since
362       // it might be used to escalate privileges.
363 #ifdef MOZ_B2G
364       mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported.";
365       return;
366 #endif
367       if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) {
368         mInvalidReason = "ServiceWorkers should be enabled.";
369         return;
370       }
371 
372       containingAppId = NO_APP_ID;
373       break;
374     }
375     default: {
376       MOZ_CRASH();
377     }
378   }
379 
380   nsCOMPtr<mozIApplication> ownApp;
381   if (!isMozBrowserElement) {
382     // mAppId corresponds to OwnOrContainingAppId; if isMozBrowserElement is
383     // false then it's ownApp otherwise it's containingApp
384     ownApp = GetAppForId(originAttributes.mAppId);
385     if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) {
386       mInvalidReason = "Got an ownAppId that didn't correspond to an app.";
387       return;
388     }
389   }
390 
391   nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId);
392   if ((containingApp == nullptr) != (containingAppId == NO_APP_ID)) {
393     mInvalidReason = "Got a containingAppId that didn't correspond to an app.";
394     return;
395   }
396 
397   bool rv;
398   rv = mTabContext.SetTabContext(isMozBrowserElement,
399                                  isPrerendered,
400                                  ownApp,
401                                  containingApp,
402                                  showAccelerators,
403                                  showFocusRings,
404                                  originAttributes,
405                                  presentationURL);
406   if (!rv) {
407     mInvalidReason = "Couldn't initialize TabContext.";
408   }
409 }
410 
411 bool
IsValid()412 MaybeInvalidTabContext::IsValid()
413 {
414   return mInvalidReason == nullptr;
415 }
416 
417 const char*
GetInvalidReason()418 MaybeInvalidTabContext::GetInvalidReason()
419 {
420   return mInvalidReason;
421 }
422 
423 const TabContext&
GetTabContext()424 MaybeInvalidTabContext::GetTabContext()
425 {
426   if (!IsValid()) {
427     MOZ_CRASH("Can't GetTabContext() if !IsValid().");
428   }
429 
430   return mTabContext;
431 }
432 
433 } // namespace dom
434 } // namespace mozilla
435