dom/ipc/TabContext.cpp
author J. Ryan Stinnett <jryans@gmail.com>
Wed, 17 Feb 2016 22:35:45 -0600
changeset 324714 76944f0d24abc3e3d968f324f18a8a3835e535dc
parent 324710 b2a21a014436551942179dd3e70c2642bb6e4e7a
child 334252 8b29568cb7e23d313b054d5cfcb02a62d24b504e
permissions -rw-r--r--
Bug 1238160 - Set frame type on TabContext. r=billm,mayhemer This change renames TabContext::IsBrowserElement to IsIsolatedMozBrowserElement. Other methods that pass these values around also have name changes. Adds TabContext::IsMozBrowserElement which is set by the frame loader for all browser frames. This is in contrast to its previous implementation, which has since been renamed IsIsolatedMozBrowserElement, since it checks isolated state in OriginAttributes. TabContext methods related to browser elements (and their callers) are updated to use IsIsolatedMozBrowserElement when check isolation / origins and IsMozBrowserElement when checking frame types. MozReview-Commit-ID: DDMZTkSn5yd

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/dom/TabContext.h"
#include "mozilla/dom/PTabContext.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/dom/TabChild.h"
#include "nsIAppsService.h"
#include "nsIScriptSecurityManager.h"
#include "nsServiceManagerUtils.h"

#define NO_APP_ID (nsIScriptSecurityManager::NO_APP_ID)

using namespace mozilla::dom::ipc;
using namespace mozilla::layout;

namespace mozilla {
namespace dom {

TabContext::TabContext()
  : mInitialized(false)
  , mIsMozBrowserElement(false)
  , mContainingAppId(NO_APP_ID)
  , mOriginAttributes()
{
}

bool
TabContext::IsMozBrowserElement() const
{
  return mIsMozBrowserElement;
}

bool
TabContext::IsIsolatedMozBrowserElement() const
{
  return mOriginAttributes.mInIsolatedMozBrowser;
}

bool
TabContext::IsMozBrowserOrApp() const
{
  return HasOwnApp() || IsMozBrowserElement();
}

uint32_t
TabContext::OwnAppId() const
{
  return mOriginAttributes.mAppId;
}

already_AddRefed<mozIApplication>
TabContext::GetOwnApp() const
{
  nsCOMPtr<mozIApplication> ownApp = mOwnApp;
  return ownApp.forget();
}

bool
TabContext::HasOwnApp() const
{
  nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
  return !!ownApp;
}

uint32_t
TabContext::BrowserOwnerAppId() const
{
  if (IsMozBrowserElement()) {
    return mContainingAppId;
  }
  return NO_APP_ID;
}

already_AddRefed<mozIApplication>
TabContext::GetBrowserOwnerApp() const
{
  nsCOMPtr<mozIApplication> ownerApp;
  if (IsMozBrowserElement()) {
    ownerApp = mContainingApp;
  }
  return ownerApp.forget();
}

bool
TabContext::HasBrowserOwnerApp() const
{
  nsCOMPtr<mozIApplication> ownerApp = GetBrowserOwnerApp();
  return !!ownerApp;
}

uint32_t
TabContext::AppOwnerAppId() const
{
  if (HasOwnApp()) {
    return mContainingAppId;
  }
  return NO_APP_ID;
}

already_AddRefed<mozIApplication>
TabContext::GetAppOwnerApp() const
{
  nsCOMPtr<mozIApplication> ownerApp;
  if (HasOwnApp()) {
    ownerApp = mContainingApp;
  }
  return ownerApp.forget();
}

bool
TabContext::HasAppOwnerApp() const
{
  nsCOMPtr<mozIApplication> ownerApp = GetAppOwnerApp();
  return !!ownerApp;
}

uint32_t
TabContext::OwnOrContainingAppId() const
{
  if (HasOwnApp()) {
    return mOriginAttributes.mAppId;
  }

  return mContainingAppId;
}

already_AddRefed<mozIApplication>
TabContext::GetOwnOrContainingApp() const
{
  nsCOMPtr<mozIApplication> ownOrContainingApp;
  if (HasOwnApp()) {
    ownOrContainingApp = mOwnApp;
  } else {
    ownOrContainingApp = mContainingApp;
  }

  return ownOrContainingApp.forget();
}

bool
TabContext::HasOwnOrContainingApp() const
{
  nsCOMPtr<mozIApplication> ownOrContainingApp = GetOwnOrContainingApp();
  return !!ownOrContainingApp;
}

bool
TabContext::SetTabContext(const TabContext& aContext)
{
  NS_ENSURE_FALSE(mInitialized, false);

  *this = aContext;
  mInitialized = true;

  return true;
}

const DocShellOriginAttributes&
TabContext::OriginAttributesRef() const
{
  return mOriginAttributes;
}

const nsACString&
TabContext::SignedPkgOriginNoSuffix() const
{
  return mSignedPkgOriginNoSuffix;
}

bool
TabContext::SetTabContext(bool aIsMozBrowserElement,
                          mozIApplication* aOwnApp,
                          mozIApplication* aAppFrameOwnerApp,
                          const DocShellOriginAttributes& aOriginAttributes,
                          const nsACString& aSignedPkgOriginNoSuffix)
{
  NS_ENSURE_FALSE(mInitialized, false);

  // Get ids for both apps and only write to our member variables after we've
  // verified that this worked.
  uint32_t ownAppId = NO_APP_ID;
  if (aOwnApp) {
    nsresult rv = aOwnApp->GetLocalId(&ownAppId);
    NS_ENSURE_SUCCESS(rv, false);
    NS_ENSURE_TRUE(ownAppId != NO_APP_ID, false);
  }

  uint32_t containingAppId = NO_APP_ID;
  if (aAppFrameOwnerApp) {
    nsresult rv = aAppFrameOwnerApp->GetLocalId(&containingAppId);
    NS_ENSURE_SUCCESS(rv, false);
    NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false);
  }

  // Veryify that app id matches mAppId passed in originAttributes
  MOZ_RELEASE_ASSERT((aOwnApp && aOriginAttributes.mAppId == ownAppId) ||
                     (aAppFrameOwnerApp && aOriginAttributes.mAppId == containingAppId) ||
                     aOriginAttributes.mAppId == NO_APP_ID);

  mInitialized = true;
  mIsMozBrowserElement = aIsMozBrowserElement;
  mOriginAttributes = aOriginAttributes;
  mContainingAppId = containingAppId;
  mOwnApp = aOwnApp;
  mContainingApp = aAppFrameOwnerApp;
  mSignedPkgOriginNoSuffix = aSignedPkgOriginNoSuffix;
  return true;
}

IPCTabContext
TabContext::AsIPCTabContext() const
{
  nsAutoCString originSuffix;
  mOriginAttributes.CreateSuffix(originSuffix);
  return IPCTabContext(FrameIPCTabContext(originSuffix,
                                          mContainingAppId,
                                          mSignedPkgOriginNoSuffix,
                                          mIsMozBrowserElement));
}

static already_AddRefed<mozIApplication>
GetAppForId(uint32_t aAppId)
{
  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
  NS_ENSURE_TRUE(appsService, nullptr);

  nsCOMPtr<mozIApplication> app;
  appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));

  return app.forget();
}

MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
  : mInvalidReason(nullptr)
{
  bool isMozBrowserElement = false;
  uint32_t containingAppId = NO_APP_ID;
  DocShellOriginAttributes originAttributes;
  nsAutoCString originSuffix;
  nsAutoCString signedPkgOriginNoSuffix;

  switch(aParams.type()) {
    case IPCTabContext::TPopupIPCTabContext: {
      const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();

      TabContext *context;
      if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
        context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent());
        if (context->IsMozBrowserElement() &&
            !ipcContext.isMozBrowserElement()) {
          // If the TabParent corresponds to a browser element, then it can only
          // open other browser elements, for security reasons.  We should have
          // checked this before calling the TabContext constructor, so this is
          // a fatal error.
          mInvalidReason = "Child is-browser process tried to "
                           "open a non-browser tab.";
          return;
        }
      } else if (ipcContext.opener().type() == PBrowserOrId::TPBrowserChild) {
        context = static_cast<TabChild*>(ipcContext.opener().get_PBrowserChild());
      } else if (ipcContext.opener().type() == PBrowserOrId::TTabId) {
        // We should never get here because this PopupIPCTabContext is only
        // used for allocating a new tab id, not for allocating a PBrowser.
        mInvalidReason = "Child process tried to open an tab without the opener information.";
        return;
      } else {
        // This should be unreachable because PopupIPCTabContext::opener is not a
        // nullable field.
        mInvalidReason = "PopupIPCTabContext::opener was null (?!).";
        return;
      }

      // Browser elements can't nest other browser elements.  So if
      // our opener is browser element, we must be a new DOM window
      // opened by it.  In that case we inherit our containing app ID
      // (if any).
      //
      // Otherwise, we're a new app window and we inherit from our
      // opener app.
      isMozBrowserElement = ipcContext.isMozBrowserElement();
      originAttributes = context->mOriginAttributes;
      if (isMozBrowserElement) {
        containingAppId = context->OwnOrContainingAppId();
      } else {
        containingAppId = context->mContainingAppId;
      }
      break;
    }
    case IPCTabContext::TFrameIPCTabContext: {
      const FrameIPCTabContext &ipcContext =
        aParams.get_FrameIPCTabContext();

      isMozBrowserElement = ipcContext.isMozBrowserElement();
      containingAppId = ipcContext.frameOwnerAppId();
      signedPkgOriginNoSuffix = ipcContext.signedPkgOriginNoSuffix();
      originSuffix = ipcContext.originSuffix();
      originAttributes.PopulateFromSuffix(originSuffix);
      break;
    }
    case IPCTabContext::TUnsafeIPCTabContext: {
      // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow.
      // It is meant as a temporary solution until service workers can
      // provide a TabChild equivalent. Don't allow this on b2g since
      // it might be used to escalate privileges.
#ifdef MOZ_B2G
      mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported.";
      return;
#endif
      if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) {
        mInvalidReason = "ServiceWorkers should be enabled.";
        return;
      }

      containingAppId = NO_APP_ID;
      break;
    }
    default: {
      MOZ_CRASH();
    }
  }

  nsCOMPtr<mozIApplication> ownApp;
  if (!isMozBrowserElement) {
    // mAppId corresponds to OwnOrContainingAppId; if isMozBrowserElement is
    // false then it's ownApp otherwise it's containingApp
    ownApp = GetAppForId(originAttributes.mAppId);
    if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) {
      mInvalidReason = "Got an ownAppId that didn't correspond to an app.";
      return;
    }
  }

  nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId);
  if ((containingApp == nullptr) != (containingAppId == NO_APP_ID)) {
    mInvalidReason = "Got a containingAppId that didn't correspond to an app.";
    return;
  }

  bool rv;
  rv = mTabContext.SetTabContext(isMozBrowserElement,
                                 ownApp,
                                 containingApp,
                                 originAttributes,
                                 signedPkgOriginNoSuffix);
  if (!rv) {
    mInvalidReason = "Couldn't initialize TabContext.";
  }
}

bool
MaybeInvalidTabContext::IsValid()
{
  return mInvalidReason == nullptr;
}

const char*
MaybeInvalidTabContext::GetInvalidReason()
{
  return mInvalidReason;
}

const TabContext&
MaybeInvalidTabContext::GetTabContext()
{
  if (!IsValid()) {
    MOZ_CRASH("Can't GetTabContext() if !IsValid().");
  }

  return mTabContext;
}

} // namespace dom
} // namespace mozilla