gecko-dev/dom/base/nsFrameLoader.cpp
Phil Ringnalda c6417cc01e Back out 5 changesets (bug 881832) for failures in browser_html_tooltip-02.js
Backed out changeset 117e8e24d714 (bug 881832)
Backed out changeset 8a0d5df41cfb (bug 881832)
Backed out changeset 6b3c5e185b04 (bug 881832)
Backed out changeset 11106afdcbe7 (bug 881832)
Backed out changeset d711b7c19a43 (bug 881832)
2016-05-11 21:08:08 -07:00

3320 lines
103 KiB
C++

/* -*- 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/. */
/*
* Class for managing loading of a subframe (creation of the docshell,
* handling of loads in it, recursion-checking).
*/
#include "base/basictypes.h"
#include "prenv.h"
#include "mozIApplication.h"
#include "nsDocShell.h"
#include "nsIDOMHTMLIFrameElement.h"
#include "nsIDOMHTMLFrameElement.h"
#include "nsIDOMMozBrowserFrame.h"
#include "nsIDOMWindow.h"
#include "nsIPresShell.h"
#include "nsIContentInlines.h"
#include "nsIContentViewer.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsPIDOMWindow.h"
#include "nsIWebNavigation.h"
#include "nsIWebProgress.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocShellLoadInfo.h"
#include "nsIBaseWindow.h"
#include "nsContentUtils.h"
#include "nsIXPConnect.h"
#include "nsUnicharUtils.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsIScrollable.h"
#include "nsFrameLoader.h"
#include "nsIDOMEventTarget.h"
#include "nsIFrame.h"
#include "nsIScrollableFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsError.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIXULWindow.h"
#include "nsIEditor.h"
#include "nsIMozBrowserFrame.h"
#include "nsIPermissionManager.h"
#include "nsISHistory.h"
#include "nsNullPrincipal.h"
#include "nsIScriptError.h"
#include "nsGlobalWindow.h"
#include "nsPIWindowRoot.h"
#include "nsLayoutUtils.h"
#include "nsView.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsGkAtoms.h"
#include "nsNameSpaceManager.h"
#include "nsThreadUtils.h"
#include "nsIDOMChromeWindow.h"
#include "nsInProcessTabChildGlobal.h"
#include "Layers.h"
#include "ClientLayerManager.h"
#include "AppProcessChecker.h"
#include "ContentParent.h"
#include "TabParent.h"
#include "mozilla/plugins/PPluginWidgetParent.h"
#include "../plugins/ipc/PluginWidgetParent.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "mozilla/dom/Element.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/layout/RenderFrameParent.h"
#include "nsIAppsService.h"
#include "GeckoProfiler.h"
#include "jsapi.h"
#include "mozilla/dom/HTMLIFrameElement.h"
#include "nsSandboxFlags.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/WebBrowserPersistLocalDocument.h"
#include "nsPrincipal.h"
#ifdef MOZ_XUL
#include "nsXULPopupManager.h"
#endif
using namespace mozilla;
using namespace mozilla::hal;
using namespace mozilla::dom;
using namespace mozilla::dom::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
typedef FrameMetrics::ViewID ViewID;
// Bug 136580: Limit to the number of nested content frames that can have the
// same URL. This is to stop content that is recursively loading
// itself. Note that "#foo" on the end of URL doesn't affect
// whether it's considered identical, but "?foo" or ";foo" are
// considered and compared.
// Bug 228829: Limit this to 1, like IE does.
#define MAX_SAME_URL_CONTENT_FRAMES 1
// Bug 8065: Limit content frame depth to some reasonable level. This
// does not count chrome frames when determining depth, nor does it
// prevent chrome recursion. Number is fairly arbitrary, but meant to
// keep number of shells to a reasonable number on accidental recursion with a
// small (but not 1) branching factor. With large branching factors the number
// of shells can rapidly become huge and run us out of memory. To solve that,
// we'd need to re-institute a fixed version of bug 98158.
#define MAX_DEPTH_CONTENT_FRAMES 10
NS_IMPL_CYCLE_COLLECTION(nsFrameLoader, mDocShell, mMessageManager, mChildMessageManager)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
NS_INTERFACE_MAP_END
nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
: mOwnerContent(aOwner)
, mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
, mDetachedSubdocFrame(nullptr)
, mRemoteBrowser(nullptr)
, mChildID(0)
, mEventMode(EVENT_MODE_NORMAL_DISPATCH)
, mIsPrerendered(false)
, mDepthTooGreat(false)
, mIsTopLevelContent(false)
, mDestroyCalled(false)
, mNeedsAsyncDestroy(false)
, mInSwap(false)
, mInShow(false)
, mHideCalled(false)
, mNetworkCreated(aNetworkCreated)
, mRemoteBrowserShown(false)
, mRemoteFrame(false)
, mClipSubdocument(true)
, mClampScrollPosition(true)
, mObservingOwnerContent(false)
, mVisible(true)
{
mRemoteFrame = ShouldUseRemoteProcess();
}
nsFrameLoader::~nsFrameLoader()
{
if (mMessageManager) {
mMessageManager->Disconnect();
}
MOZ_RELEASE_ASSERT(mDestroyCalled);
}
nsFrameLoader*
nsFrameLoader::Create(Element* aOwner, bool aNetworkCreated)
{
NS_ENSURE_TRUE(aOwner, nullptr);
nsIDocument* doc = aOwner->OwnerDoc();
NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
((!doc->IsLoadedAsData() && aOwner->GetComposedDoc()) ||
doc->IsStaticDocument()),
nullptr);
return new nsFrameLoader(aOwner, aNetworkCreated);
}
NS_IMETHODIMP
nsFrameLoader::LoadFrame()
{
NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED);
nsAutoString src;
bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
if (isSrcdoc) {
src.AssignLiteral("about:srcdoc");
}
else {
GetURL(src);
src.Trim(" \t\n\r");
if (src.IsEmpty()) {
// If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
// then we will not use 'about:blank' as fallback but return early without
// starting a load if no 'src' attribute is given (or it's empty).
if (mOwnerContent->IsXULElement() &&
mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
nsGkAtoms::_true, eCaseMatters)) {
return NS_OK;
}
src.AssignLiteral("about:blank");
}
}
nsIDocument* doc = mOwnerContent->OwnerDoc();
if (doc->IsStaticDocument()) {
return NS_OK;
}
nsCOMPtr<nsIURI> base_uri = mOwnerContent->GetBaseURI();
const nsAFlatCString &doc_charset = doc->GetDocumentCharacterSet();
const char *charset = doc_charset.IsEmpty() ? nullptr : doc_charset.get();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), src, charset, base_uri);
// If the URI was malformed, try to recover by loading about:blank.
if (rv == NS_ERROR_MALFORMED_URI) {
rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"),
charset, base_uri);
}
if (NS_SUCCEEDED(rv)) {
rv = LoadURI(uri);
}
if (NS_FAILED(rv)) {
FireErrorEvent();
return rv;
}
return NS_OK;
}
void
nsFrameLoader::FireErrorEvent()
{
if (!mOwnerContent) {
return;
}
RefPtr<AsyncEventDispatcher > loadBlockingAsyncDispatcher =
new LoadBlockingAsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("error"),
false, false);
loadBlockingAsyncDispatcher->PostDOMEvent();
}
NS_IMETHODIMP
nsFrameLoader::LoadURI(nsIURI* aURI)
{
if (!aURI)
return NS_ERROR_INVALID_POINTER;
NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc();
nsresult rv = CheckURILoad(aURI);
NS_ENSURE_SUCCESS(rv, rv);
mURIToLoad = aURI;
rv = doc->InitializeFrameLoader(this);
if (NS_FAILED(rv)) {
mURIToLoad = nullptr;
}
return rv;
}
NS_IMETHODIMP
nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI, const nsACString& aPackageId)
{
RefPtr<TabParent> tp = nullptr;
MutableTabContext context;
nsresult rv = GetNewTabContext(&context, aURI, aPackageId);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> ownerElement = mOwnerContent;
tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr);
if (!tp) {
return NS_ERROR_FAILURE;
}
mRemoteBrowserShown = false;
rv = SwapRemoteBrowser(tp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
LoadURI(aURI);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetIsPrerendered()
{
MOZ_ASSERT(!mDocShell, "Please call SetIsPrerendered before docShell is created");
mIsPrerendered = true;
return NS_OK;
}
nsresult
nsFrameLoader::ReallyStartLoading()
{
nsresult rv = ReallyStartLoadingInternal();
if (NS_FAILED(rv)) {
FireErrorEvent();
}
return rv;
}
nsresult
nsFrameLoader::ReallyStartLoadingInternal()
{
NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc());
PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading",
js::ProfileEntry::Category::OTHER);
if (IsRemoteFrame()) {
if (!mRemoteBrowser && !TryRemoteBrowser()) {
NS_WARNING("Couldn't create child process for iframe.");
return NS_ERROR_FAILURE;
}
// FIXME get error codes from child
mRemoteBrowser->LoadURL(mURIToLoad);
if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) {
NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n");
}
return NS_OK;
}
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded with a null mDocShell");
// Just to be safe, recheck uri.
rv = CheckURILoad(mURIToLoad);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
mDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
// If this frame is sandboxed with respect to origin we will set it up with
// a null principal later in nsDocShell::DoURILoad.
// We do it there to correctly sandbox content that was loaded into
// the frame via other methods than the src attribute.
// We'll use our principal, not that of the document loaded inside us. This
// is very important; needed to prevent XSS attacks on documents loaded in
// subframes!
loadInfo->SetOwner(mOwnerContent->NodePrincipal());
nsCOMPtr<nsIURI> referrer;
nsAutoString srcdoc;
bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc,
srcdoc);
if (isSrcdoc) {
nsAutoString referrerStr;
mOwnerContent->OwnerDoc()->GetReferrer(referrerStr);
rv = NS_NewURI(getter_AddRefs(referrer), referrerStr);
loadInfo->SetSrcdocData(srcdoc);
nsCOMPtr<nsIURI> baseURI = mOwnerContent->GetBaseURI();
loadInfo->SetBaseURI(baseURI);
}
else {
rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
NS_ENSURE_SUCCESS(rv, rv);
}
// Use referrer as long as it is not an nsNullPrincipalURI.
// We could add a method such as GetReferrerURI to principals to make this
// cleaner, but given that we need to start using Source Browsing Context for
// referrer (see Bug 960639) this may be wasted effort at this stage.
if (referrer) {
bool isNullPrincipalScheme;
rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
loadInfo->SetReferrer(referrer);
}
}
// get referrer policy for this iframe:
// first load document wide policy, then
// load iframe referrer attribute if enabled in preferences
// per element referrer overrules document wide referrer if enabled
net::ReferrerPolicy referrerPolicy = mOwnerContent->OwnerDoc()->GetReferrerPolicy();
HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
if (iframe) {
net::ReferrerPolicy iframeReferrerPolicy = iframe->GetReferrerPolicyAsEnum();
if (iframeReferrerPolicy != net::RP_Unset) {
referrerPolicy = iframeReferrerPolicy;
}
}
loadInfo->SetReferrerPolicy(referrerPolicy);
// Default flags:
int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
// Flags for browser frame:
if (OwnerIsMozBrowserFrame()) {
flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
}
// Kick off the load...
bool tmpState = mNeedsAsyncDestroy;
mNeedsAsyncDestroy = true;
nsCOMPtr<nsIURI> uriToLoad = mURIToLoad;
rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false);
mNeedsAsyncDestroy = tmpState;
mURIToLoad = nullptr;
NS_ENSURE_SUCCESS(rv, rv);
// Track the appId's reference count if this frame is in-process
ResetPermissionManagerStatus();
return NS_OK;
}
nsresult
nsFrameLoader::CheckURILoad(nsIURI* aURI)
{
// Check for security. The fun part is trying to figure out what principals
// to use. The way I figure it, if we're doing a LoadFrame() accidentally
// (eg someone created a frame/iframe node, we're being parsed, XUL iframes
// are being reframed, etc.) then we definitely want to use the node
// principal of mOwnerContent for security checks. If, on the other hand,
// someone's setting the src on our owner content, or created it via script,
// or whatever, then they can clearly access it... and we should still use
// the principal of mOwnerContent. I don't think that leads to privilege
// escalation, and it's reasonably guaranteed to not lead to XSS issues
// (since caller can already access mOwnerContent in this case). So just use
// the principal of mOwnerContent no matter what. If script wants to run
// things with its own permissions, which differ from those of mOwnerContent
// (which means the script is privileged in some way) it should set
// window.location instead.
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
// Get our principal
nsIPrincipal* principal = mOwnerContent->NodePrincipal();
// Check if we are allowed to load absURL
nsresult rv =
secMan->CheckLoadURIWithPrincipal(principal, aURI,
nsIScriptSecurityManager::STANDARD);
if (NS_FAILED(rv)) {
return rv; // We're not
}
// Bail out if this is an infinite recursion scenario
if (IsRemoteFrame()) {
return NS_OK;
}
return CheckForRecursiveLoad(aURI);
}
NS_IMETHODIMP
nsFrameLoader::GetDocShell(nsIDocShell **aDocShell)
{
*aDocShell = nullptr;
nsresult rv = NS_OK;
if (IsRemoteFrame()) {
return rv;
}
// If we have an owner, make sure we have a docshell and return
// that. If not, we're most likely in the middle of being torn down,
// then we just return null.
if (mOwnerContent) {
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
}
*aDocShell = mDocShell;
NS_IF_ADDREF(*aDocShell);
return rv;
}
static void
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(nsIDocShellTreeItem* aItem,
nsIDocShellTreeOwner* aOwner,
EventTarget* aHandler)
{
NS_PRECONDITION(aItem, "Must have item");
aItem->SetTreeOwner(aOwner);
int32_t childCount = 0;
aItem->GetChildCount(&childCount);
for (int32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShellTreeItem> item;
aItem->GetChildAt(i, getter_AddRefs(item));
if (aHandler) {
nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
shell->SetChromeEventHandler(aHandler);
}
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
}
}
/**
* Set the type of the treeitem and hook it up to the treeowner.
* @param aItem the treeitem we're working with
* @param aTreeOwner the relevant treeowner; might be null
* @param aParentType the nsIDocShellTreeItem::GetType of our parent docshell
* @param aParentNode if non-null, the docshell we should be added as a child to
*
* @return whether aItem is top-level content
*/
bool
nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
nsIDocShellTreeOwner* aOwner,
int32_t aParentType,
nsIDocShell* aParentNode)
{
NS_PRECONDITION(aItem, "Must have docshell treeitem");
NS_PRECONDITION(mOwnerContent, "Must have owning content");
nsAutoString value;
bool isContent = false;
mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
// we accept "content" and "content-xxx" values.
// at time of writing, we expect "xxx" to be "primary" or "targetable", but
// someday it might be an integer expressing priority or something else.
isContent = value.LowerCaseEqualsLiteral("content") ||
StringBeginsWith(value, NS_LITERAL_STRING("content-"),
nsCaseInsensitiveStringComparator());
// Force mozbrowser frames to always be typeContent, even if the
// mozbrowser interfaces are disabled.
nsCOMPtr<nsIDOMMozBrowserFrame> mozbrowser =
do_QueryInterface(mOwnerContent);
if (mozbrowser) {
bool isMozbrowser = false;
mozbrowser->GetMozbrowser(&isMozbrowser);
isContent |= isMozbrowser;
}
if (isContent) {
// The web shell's type is content.
aItem->SetItemType(nsIDocShellTreeItem::typeContent);
} else {
// Inherit our type from our parent docshell. If it is
// chrome, we'll be chrome. If it is content, we'll be
// content.
aItem->SetItemType(aParentType);
}
// Now that we have our type set, add ourselves to the parent, as needed.
if (aParentNode) {
aParentNode->AddChild(aItem);
}
bool retval = false;
if (aParentType == nsIDocShellTreeItem::typeChrome && isContent) {
retval = true;
bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
if (aOwner) {
bool is_targetable = is_primary ||
value.LowerCaseEqualsLiteral("content-targetable");
mOwnerContent->AddMutationObserver(this);
mObservingOwnerContent = true;
aOwner->ContentShellAdded(aItem, is_primary, is_targetable, value);
}
}
return retval;
}
static bool
AllDescendantsOfType(nsIDocShellTreeItem* aParentItem, int32_t aType)
{
int32_t childCount = 0;
aParentItem->GetChildCount(&childCount);
for (int32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShellTreeItem> kid;
aParentItem->GetChildAt(i, getter_AddRefs(kid));
if (kid->ItemType() != aType || !AllDescendantsOfType(kid, aType)) {
return false;
}
}
return true;
}
/**
* A class that automatically sets mInShow to false when it goes
* out of scope.
*/
class MOZ_RAII AutoResetInShow {
private:
nsFrameLoader* mFrameLoader;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mFrameLoader(aFrameLoader)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoResetInShow() { mFrameLoader->mInShow = false; }
};
bool
nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
int32_t scrollbarPrefX, int32_t scrollbarPrefY,
nsSubDocumentFrame* frame)
{
if (mInShow) {
return false;
}
// Reset mInShow if we exit early.
AutoResetInShow resetInShow(this);
mInShow = true;
ScreenIntSize size = frame->GetSubdocumentSize();
if (IsRemoteFrame()) {
return ShowRemoteFrame(size, frame);
}
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return false;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
return false;
}
mDocShell->SetMarginWidth(marginWidth);
mDocShell->SetMarginHeight(marginHeight);
nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
if (sc) {
sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
scrollbarPrefX);
sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
scrollbarPrefY);
}
nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
if (presShell) {
// Ensure root scroll frame is reflowed in case scroll preferences or
// margins have changed
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
if (rootScrollFrame) {
presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
NS_FRAME_IS_DIRTY);
}
return true;
}
nsView* view = frame->EnsureInnerView();
if (!view)
return false;
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mDocShell);
NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow.");
baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0,
size.width, size.height);
// This is kinda whacky, this "Create()" call doesn't really
// create anything, one starts to wonder why this was named
// "Create"...
baseWindow->Create();
baseWindow->SetVisibility(true);
NS_ENSURE_TRUE(mDocShell, false);
// Trigger editor re-initialization if midas is turned on in the
// sub-document. This shouldn't be necessary, but given the way our
// editor works, it is. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=284245
presShell = mDocShell->GetPresShell();
if (presShell) {
nsCOMPtr<nsIDOMHTMLDocument> doc =
do_QueryInterface(presShell->GetDocument());
if (doc) {
nsAutoString designMode;
doc->GetDesignMode(designMode);
if (designMode.EqualsLiteral("on")) {
// Hold on to the editor object to let the document reattach to the
// same editor object, instead of creating a new one.
nsCOMPtr<nsIEditor> editor;
nsresult rv = mDocShell->GetEditor(getter_AddRefs(editor));
NS_ENSURE_SUCCESS(rv, false);
doc->SetDesignMode(NS_LITERAL_STRING("off"));
doc->SetDesignMode(NS_LITERAL_STRING("on"));
} else {
// Re-initialize the presentation for contenteditable documents
bool editable = false,
hasEditingSession = false;
mDocShell->GetEditable(&editable);
mDocShell->GetHasEditingSession(&hasEditingSession);
nsCOMPtr<nsIEditor> editor;
mDocShell->GetEditor(getter_AddRefs(editor));
if (editable && hasEditingSession && editor) {
editor->PostCreate();
}
}
}
}
mInShow = false;
if (mHideCalled) {
mHideCalled = false;
Hide();
return false;
}
return true;
}
void
nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
uint32_t aMarginHeight)
{
// We assume that the margins are always zero for remote frames.
if (IsRemoteFrame())
return;
// If there's no docshell, we're probably not up and running yet.
// nsFrameLoader::Show() will take care of setting the right
// margins.
if (!mDocShell)
return;
// Set the margins
mDocShell->SetMarginWidth(aMarginWidth);
mDocShell->SetMarginHeight(aMarginHeight);
// Trigger a restyle if there's a prescontext
// FIXME: This could do something much less expensive.
RefPtr<nsPresContext> presContext;
mDocShell->GetPresContext(getter_AddRefs(presContext));
if (presContext)
presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
}
bool
nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
nsSubDocumentFrame *aFrame)
{
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames.");
if (!mRemoteBrowser && !TryRemoteBrowser()) {
NS_ERROR("Couldn't create child process.");
return false;
}
// FIXME/bug 589337: Show()/Hide() is pretty expensive for
// cross-process layers; need to figure out what behavior we really
// want here. For now, hack.
if (!mRemoteBrowserShown) {
if (!mOwnerContent ||
!mOwnerContent->GetComposedDoc()) {
return false;
}
RefPtr<layers::LayerManager> layerManager =
nsContentUtils::LayerManagerForDocument(mOwnerContent->GetComposedDoc());
if (!layerManager) {
// This is just not going to work.
return false;
}
nsPIDOMWindowOuter* win = mOwnerContent->OwnerDoc()->GetWindow();
bool parentIsActive = false;
if (win) {
nsCOMPtr<nsPIWindowRoot> windowRoot =
nsGlobalWindow::Cast(win)->GetTopWindowRoot();
if (windowRoot) {
nsPIDOMWindowOuter* topWin = windowRoot->GetWindow();
parentIsActive = topWin && topWin->IsActive();
}
}
mRemoteBrowser->Show(size, parentIsActive);
mRemoteBrowserShown = true;
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"remote-browser-shown", nullptr);
}
} else {
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
// Don't show remote iframe if we are waiting for the completion of reflow.
if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
}
return true;
}
void
nsFrameLoader::Hide()
{
if (mHideCalled) {
return;
}
if (mInShow) {
mHideCalled = true;
return;
}
if (!mDocShell)
return;
nsCOMPtr<nsIContentViewer> contentViewer;
mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
if (contentViewer)
contentViewer->SetSticky(false);
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
NS_ASSERTION(baseWin,
"Found an nsIDocShell which doesn't implement nsIBaseWindow.");
baseWin->SetVisibility(false);
baseWin->SetParentWidget(nullptr);
}
nsresult
nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
RefPtr<nsFrameLoader>& aFirstToSwap,
RefPtr<nsFrameLoader>& aSecondToSwap)
{
MOZ_ASSERT(NS_IsMainThread());
Element* ourContent = mOwnerContent;
Element* otherContent = aOther->mOwnerContent;
if (!ourContent || !otherContent) {
// Can't handle this
return NS_ERROR_NOT_IMPLEMENTED;
}
// Make sure there are no same-origin issues
bool equal;
nsresult rv =
ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
if (NS_FAILED(rv) || !equal) {
// Security problems loom. Just bail on it all
return NS_ERROR_DOM_SECURITY_ERR;
}
nsIDocument* ourDoc = ourContent->GetComposedDoc();
nsIDocument* otherDoc = otherContent->GetComposedDoc();
if (!ourDoc || !otherDoc) {
// Again, how odd, given that we had docshells
return NS_ERROR_NOT_IMPLEMENTED;
}
nsIPresShell* ourShell = ourDoc->GetShell();
nsIPresShell* otherShell = otherDoc->GetShell();
if (!ourShell || !otherShell) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (mRemoteBrowser->IsIsolatedMozBrowserElement() !=
aOther->mRemoteBrowser->IsIsolatedMozBrowserElement() ||
mRemoteBrowser->HasOwnApp() != aOther->mRemoteBrowser->HasOwnApp()) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// When we swap docShells, maybe we have to deal with a new page created just
// for this operation. In this case, the browser code should already have set
// the correct userContextId attribute value in the owning XULElement, but our
// docShell, that has been created way before) doesn't know that that
// happened.
// This is the reason why now we must retrieve the correct value from the
// usercontextid attribute before comparing our originAttributes with the
// other one.
DocShellOriginAttributes ourOriginAttributes =
mRemoteBrowser->OriginAttributesRef();
rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
NS_ENSURE_SUCCESS(rv,rv);
DocShellOriginAttributes otherOriginAttributes =
aOther->mRemoteBrowser->OriginAttributesRef();
rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
NS_ENSURE_SUCCESS(rv,rv);
if (ourOriginAttributes != otherOriginAttributes) {
return NS_ERROR_NOT_IMPLEMENTED;
}
bool ourHasHistory =
mIsTopLevelContent &&
ourContent->IsXULElement(nsGkAtoms::browser) &&
!ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
bool otherHasHistory =
aOther->mIsTopLevelContent &&
otherContent->IsXULElement(nsGkAtoms::browser) &&
!otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
if (ourHasHistory != otherHasHistory) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (mInSwap || aOther->mInSwap) {
return NS_ERROR_NOT_IMPLEMENTED;
}
mInSwap = aOther->mInSwap = true;
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
if (!ourFrame || !otherFrame) {
mInSwap = aOther->mInSwap = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
if (!ourFrameFrame) {
mInSwap = aOther->mInSwap = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
if (NS_FAILED(rv)) {
mInSwap = aOther->mInSwap = false;
return rv;
}
mRemoteBrowser->SwapLayerTreeObservers(aOther->mRemoteBrowser);
nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
aOther->mRemoteBrowser->GetBrowserDOMWindow();
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
mRemoteBrowser->GetBrowserDOMWindow();
if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
return NS_ERROR_NOT_IMPLEMENTED;
}
aOther->mRemoteBrowser->SetBrowserDOMWindow(browserDOMWindow);
mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow);
// Native plugin windows used by this remote content need to be reparented.
if (nsPIDOMWindowOuter* newWin = ourDoc->GetWindow()) {
RefPtr<nsIWidget> newParent = nsGlobalWindow::Cast(newWin)->GetMainWidget();
const ManagedContainer<mozilla::plugins::PPluginWidgetParent>& plugins =
aOther->mRemoteBrowser->ManagedPPluginWidgetParent();
for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) {
static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())->SetParent(newParent);
}
}
MaybeUpdatePrimaryTabParent(eTabParentRemoved);
aOther->MaybeUpdatePrimaryTabParent(eTabParentRemoved);
SetOwnerContent(otherContent);
aOther->SetOwnerContent(ourContent);
mRemoteBrowser->SetOwnerElement(otherContent);
aOther->mRemoteBrowser->SetOwnerElement(ourContent);
MaybeUpdatePrimaryTabParent(eTabParentChanged);
aOther->MaybeUpdatePrimaryTabParent(eTabParentChanged);
RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
// Swap and setup things in parent message managers.
if (mMessageManager) {
mMessageManager->SetCallback(aOther);
}
if (aOther->mMessageManager) {
aOther->mMessageManager->SetCallback(this);
}
mMessageManager.swap(aOther->mMessageManager);
aFirstToSwap.swap(aSecondToSwap);
ourFrameFrame->EndSwapDocShells(otherFrame);
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourDoc->FlushPendingNotifications(Flush_Layout);
otherDoc->FlushPendingNotifications(Flush_Layout);
// Initialize browser API if needed now that owner content has changed.
InitializeBrowserAPI();
aOther->InitializeBrowserAPI();
mInSwap = aOther->mInSwap = false;
// Send an updated tab context since owner content type may have changed.
MutableTabContext ourContext;
rv = GetNewTabContext(&ourContext);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MutableTabContext otherContext;
rv = aOther->GetNewTabContext(&otherContext);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader(
ourContext.AsIPCTabContext());
Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader(
otherContext.AsIPCTabContext());
return NS_OK;
}
class MOZ_RAII AutoResetInFrameSwap final
{
public:
AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
nsFrameLoader* aOtherFrameLoader,
nsDocShell* aThisDocShell,
nsDocShell* aOtherDocShell,
EventTarget* aThisEventTarget,
EventTarget* aOtherEventTarget
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mThisFrameLoader(aThisFrameLoader)
, mOtherFrameLoader(aOtherFrameLoader)
, mThisDocShell(aThisDocShell)
, mOtherDocShell(aOtherDocShell)
, mThisEventTarget(aThisEventTarget)
, mOtherEventTarget(aOtherEventTarget)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mThisFrameLoader->mInSwap = true;
mOtherFrameLoader->mInSwap = true;
mThisDocShell->SetInFrameSwap(true);
mOtherDocShell->SetInFrameSwap(true);
// Fire pageshow events on still-loading pages, and then fire pagehide
// events. Note that we do NOT fire these in the normal way, but just fire
// them on the chrome event handlers.
nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false);
nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false);
nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget);
nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget);
}
~AutoResetInFrameSwap()
{
nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true);
nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true);
mThisFrameLoader->mInSwap = false;
mOtherFrameLoader->mInSwap = false;
mThisDocShell->SetInFrameSwap(false);
mOtherDocShell->SetInFrameSwap(false);
}
private:
RefPtr<nsFrameLoader> mThisFrameLoader;
RefPtr<nsFrameLoader> mOtherFrameLoader;
RefPtr<nsDocShell> mThisDocShell;
RefPtr<nsDocShell> mOtherDocShell;
nsCOMPtr<EventTarget> mThisEventTarget;
nsCOMPtr<EventTarget> mOtherEventTarget;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
nsresult
nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
RefPtr<nsFrameLoader>& aFirstToSwap,
RefPtr<nsFrameLoader>& aSecondToSwap)
{
NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) ||
(aFirstToSwap == aOther && aSecondToSwap == this),
"Swapping some sort of random loaders?");
NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
NS_WARNING("Swapping remote and non-remote frames is not currently supported");
return NS_ERROR_NOT_IMPLEMENTED;
}
Element* ourContent = mOwnerContent;
Element* otherContent = aOther->mOwnerContent;
if (!ourContent || !otherContent) {
// Can't handle this
return NS_ERROR_NOT_IMPLEMENTED;
}
bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
if (ourHasSrcdoc || otherHasSrcdoc) {
// Ignore this case entirely for now, since we support XUL <-> HTML swapping
return NS_ERROR_NOT_IMPLEMENTED;
}
bool ourPassPointerEvents =
ourContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::mozpasspointerevents,
nsGkAtoms::_true,
eCaseMatters);
bool otherPassPointerEvents =
otherContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::mozpasspointerevents,
nsGkAtoms::_true,
eCaseMatters);
if (ourPassPointerEvents != otherPassPointerEvents) {
return NS_ERROR_NOT_IMPLEMENTED;
}
bool ourFullscreenAllowed =
ourContent->IsXULElement() ||
(OwnerIsMozBrowserOrAppFrame() &&
(ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
bool otherFullscreenAllowed =
otherContent->IsXULElement() ||
(aOther->OwnerIsMozBrowserOrAppFrame() &&
(otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
if (ourFullscreenAllowed != otherFullscreenAllowed) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Divert to a separate path for the remaining steps in the remote case
if (IsRemoteFrame()) {
MOZ_ASSERT(aOther->IsRemoteFrame());
return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
}
// Make sure there are no same-origin issues
bool equal;
nsresult rv =
ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
if (NS_FAILED(rv) || !equal) {
// Security problems loom. Just bail on it all
return NS_ERROR_DOM_SECURITY_ERR;
}
RefPtr<nsDocShell> ourDocshell = static_cast<nsDocShell*>(GetExistingDocShell());
RefPtr<nsDocShell> otherDocshell = static_cast<nsDocShell*>(aOther->GetExistingDocShell());
if (!ourDocshell || !otherDocshell) {
// How odd
return NS_ERROR_NOT_IMPLEMENTED;
}
// To avoid having to mess with session history, avoid swapping
// frameloaders that don't correspond to root same-type docshells,
// unless both roots have session history disabled.
nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
ourDocshell->GetSameTypeRootTreeItem(getter_AddRefs(ourRootTreeItem));
otherDocshell->GetSameTypeRootTreeItem(getter_AddRefs(otherRootTreeItem));
nsCOMPtr<nsIWebNavigation> ourRootWebnav =
do_QueryInterface(ourRootTreeItem);
nsCOMPtr<nsIWebNavigation> otherRootWebnav =
do_QueryInterface(otherRootTreeItem);
if (!ourRootWebnav || !otherRootWebnav) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsISHistory> ourHistory;
nsCOMPtr<nsISHistory> otherHistory;
ourRootWebnav->GetSessionHistory(getter_AddRefs(ourHistory));
otherRootWebnav->GetSessionHistory(getter_AddRefs(otherHistory));
if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
(ourHistory || otherHistory)) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Also make sure that the two docshells are the same type. Otherwise
// swapping is certainly not safe. If this needs to be changed then
// the code below needs to be audited as it assumes identical types.
int32_t ourType = ourDocshell->ItemType();
int32_t otherType = otherDocshell->ItemType();
if (ourType != otherType) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// One more twist here. Setting up the right treeowners in a heterogeneous
// tree is a bit of a pain. So make sure that if ourType is not
// nsIDocShellTreeItem::typeContent then all of our descendants are the same
// type as us.
if (ourType != nsIDocShellTreeItem::typeContent &&
(!AllDescendantsOfType(ourDocshell, ourType) ||
!AllDescendantsOfType(otherDocshell, otherType))) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Save off the tree owners, frame elements, chrome event handlers, and
// docshell and document parents before doing anything else.
nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
// Note: it's OK to have null treeowners.
nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
ourDocshell->GetParent(getter_AddRefs(ourParentItem));
otherDocshell->GetParent(getter_AddRefs(otherParentItem));
if (!ourParentItem || !otherParentItem) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Make sure our parents are the same type too
int32_t ourParentType = ourParentItem->ItemType();
int32_t otherParentType = otherParentItem->ItemType();
if (ourParentType != otherParentType) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow();
nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow();
nsCOMPtr<Element> ourFrameElement =
ourWindow->GetFrameElementInternal();
nsCOMPtr<Element> otherFrameElement =
otherWindow->GetFrameElementInternal();
nsCOMPtr<EventTarget> ourChromeEventHandler =
do_QueryInterface(ourWindow->GetChromeEventHandler());
nsCOMPtr<EventTarget> otherChromeEventHandler =
do_QueryInterface(otherWindow->GetChromeEventHandler());
nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();
NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
SameCOMIdentity(otherFrameElement, otherContent) &&
SameCOMIdentity(ourChromeEventHandler, ourContent) &&
SameCOMIdentity(otherChromeEventHandler, otherContent),
"How did that happen, exactly?");
nsCOMPtr<nsIDocument> ourChildDocument = ourWindow->GetExtantDoc();
nsCOMPtr<nsIDocument> otherChildDocument = otherWindow ->GetExtantDoc();
if (!ourChildDocument || !otherChildDocument) {
// This shouldn't be happening
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsIDocument> ourParentDocument =
ourChildDocument->GetParentDocument();
nsCOMPtr<nsIDocument> otherParentDocument =
otherChildDocument->GetParentDocument();
// Make sure to swap docshells between the two frames.
nsIDocument* ourDoc = ourContent->GetComposedDoc();
nsIDocument* otherDoc = otherContent->GetComposedDoc();
if (!ourDoc || !otherDoc) {
// Again, how odd, given that we had docshells
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
nsIPresShell* ourShell = ourDoc->GetShell();
nsIPresShell* otherShell = otherDoc->GetShell();
if (!ourShell || !otherShell) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (ourDocshell->GetIsIsolatedMozBrowserElement() !=
otherDocshell->GetIsIsolatedMozBrowserElement() ||
ourDocshell->GetIsApp() != otherDocshell->GetIsApp()) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// When we swap docShells, maybe we have to deal with a new page created just
// for this operation. In this case, the browser code should already have set
// the correct userContextId attribute value in the owning XULElement, but our
// docShell, that has been created way before) doesn't know that that
// happened.
// This is the reason why now we must retrieve the correct value from the
// usercontextid attribute before comparing our originAttributes with the
// other one.
DocShellOriginAttributes ourOriginAttributes =
ourDocshell->GetOriginAttributes();
rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
NS_ENSURE_SUCCESS(rv,rv);
DocShellOriginAttributes otherOriginAttributes =
otherDocshell->GetOriginAttributes();
rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
NS_ENSURE_SUCCESS(rv,rv);
if (ourOriginAttributes != otherOriginAttributes) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (mInSwap || aOther->mInSwap) {
return NS_ERROR_NOT_IMPLEMENTED;
}
AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
ourEventTarget, otherEventTarget);
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
if (!ourFrame || !otherFrame) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
if (!ourFrameFrame) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// OK. First begin to swap the docshells in the two nsIFrames
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
if (NS_FAILED(rv)) {
return rv;
}
// Now move the docshells to the right docshell trees. Note that this
// resets their treeowners to null.
ourParentItem->RemoveChild(ourDocshell);
otherParentItem->RemoveChild(otherDocshell);
if (ourType == nsIDocShellTreeItem::typeContent) {
ourOwner->ContentShellRemoved(ourDocshell);
otherOwner->ContentShellRemoved(otherDocshell);
}
ourParentItem->AddChild(otherDocshell);
otherParentItem->AddChild(ourDocshell);
// Restore the correct chrome event handlers.
ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
// Restore the correct treeowners
// (and also chrome event handlers for content frames only).
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(ourDocshell, otherOwner,
ourType == nsIDocShellTreeItem::typeContent ? otherChromeEventHandler.get() : nullptr);
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(otherDocshell, ourOwner,
ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler.get() : nullptr);
// Switch the owner content before we start calling AddTreeItemToTreeOwner.
// Note that we rely on this to deal with setting mObservingOwnerContent to
// false and calling RemoveMutationObserver as needed.
SetOwnerContent(otherContent);
aOther->SetOwnerContent(ourContent);
AddTreeItemToTreeOwner(ourDocshell, otherOwner, otherParentType, nullptr);
aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner, ourParentType,
nullptr);
// SetSubDocumentFor nulls out parent documents on the old child doc if a
// new non-null document is passed in, so just go ahead and remove both
// kids before reinserting in the parent subdoc maps, to avoid
// complications.
ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);
ourWindow->SetFrameElementInternal(otherFrameElement);
otherWindow->SetFrameElementInternal(ourFrameElement);
RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
// Swap pointers in child message managers.
if (mChildMessageManager) {
nsInProcessTabChildGlobal* tabChild =
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
tabChild->SetOwner(otherContent);
tabChild->SetChromeMessageManager(otherMessageManager);
}
if (aOther->mChildMessageManager) {
nsInProcessTabChildGlobal* otherTabChild =
static_cast<nsInProcessTabChildGlobal*>(aOther->mChildMessageManager.get());
otherTabChild->SetOwner(ourContent);
otherTabChild->SetChromeMessageManager(ourMessageManager);
}
// Swap and setup things in parent message managers.
if (mMessageManager) {
mMessageManager->SetCallback(aOther);
}
if (aOther->mMessageManager) {
aOther->mMessageManager->SetCallback(this);
}
mMessageManager.swap(aOther->mMessageManager);
aFirstToSwap.swap(aSecondToSwap);
// Drop any cached content viewers in the two session histories.
nsCOMPtr<nsISHistoryInternal> ourInternalHistory =
do_QueryInterface(ourHistory);
nsCOMPtr<nsISHistoryInternal> otherInternalHistory =
do_QueryInterface(otherHistory);
if (ourInternalHistory) {
ourInternalHistory->EvictAllContentViewers();
}
if (otherInternalHistory) {
otherInternalHistory->EvictAllContentViewers();
}
NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
otherFrame == otherContent->GetPrimaryFrame(),
"changed primary frame");
ourFrameFrame->EndSwapDocShells(otherFrame);
// If the content being swapped came from windows on two screens with
// incompatible backing resolution (e.g. dragging a tab between windows on
// hi-dpi and low-dpi screens), it will have style data that is based on
// the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
// backing scale factor may have changed. (Bug 822266)
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourParentDocument->FlushPendingNotifications(Flush_Layout);
otherParentDocument->FlushPendingNotifications(Flush_Layout);
// Initialize browser API if needed now that owner content has changed
InitializeBrowserAPI();
aOther->InitializeBrowserAPI();
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::Destroy()
{
StartDestroy();
return NS_OK;
}
class nsFrameLoaderDestroyRunnable : public Runnable
{
enum DestroyPhase
{
// See the implementation of Run for an explanation of these phases.
eDestroyDocShell,
eWaitForUnloadMessage,
eDestroyComplete
};
RefPtr<nsFrameLoader> mFrameLoader;
DestroyPhase mPhase;
public:
explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
: mFrameLoader(aFrameLoader), mPhase(eDestroyDocShell) {}
NS_IMETHODIMP Run() override;
};
void
nsFrameLoader::StartDestroy()
{
// nsFrameLoader::StartDestroy is called just before the frameloader is
// detached from the <browser> element. Destruction continues in phases via
// the nsFrameLoaderDestroyRunnable.
if (mDestroyCalled) {
return;
}
mDestroyCalled = true;
// After this point, we return an error when trying to send a message using
// the message manager on the frame.
if (mMessageManager) {
mMessageManager->Close();
}
// Retain references to the <browser> element and the frameloader in case we
// receive any messages from the message manager on the frame. These
// references are dropped in DestroyComplete.
if (mChildMessageManager || mRemoteBrowser) {
mOwnerContentStrong = mOwnerContent;
if (mRemoteBrowser) {
mRemoteBrowser->CacheFrameLoader(this);
}
if (mChildMessageManager) {
mChildMessageManager->CacheFrameLoader(this);
}
}
// If the TabParent has installed any event listeners on the window, this is
// its last chance to remove them while we're still in the document.
if (mRemoteBrowser) {
mRemoteBrowser->RemoveWindowListeners();
}
nsCOMPtr<nsIDocument> doc;
bool dynamicSubframeRemoval = false;
if (mOwnerContent) {
doc = mOwnerContent->OwnerDoc();
dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion();
doc->SetSubDocumentFor(mOwnerContent, nullptr);
MaybeUpdatePrimaryTabParent(eTabParentRemoved);
SetOwnerContent(nullptr);
}
// Seems like this is a dynamic frame removal.
if (dynamicSubframeRemoval) {
if (mDocShell) {
mDocShell->RemoveFromSessionHistory();
}
}
// Let the tree owner know we're gone.
if (mIsTopLevelContent) {
if (mDocShell) {
nsCOMPtr<nsIDocShellTreeItem> parentItem;
mDocShell->GetParent(getter_AddRefs(parentItem));
nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
if (owner) {
owner->ContentShellRemoved(mDocShell);
}
}
}
// Let our window know that we are gone
if (mDocShell) {
nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
if (win_private) {
win_private->SetFrameElementInternal(nullptr);
}
}
nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this);
if (mNeedsAsyncDestroy || !doc ||
NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
NS_DispatchToCurrentThread(destroyRunnable);
}
}
nsresult
nsFrameLoaderDestroyRunnable::Run()
{
switch (mPhase) {
case eDestroyDocShell:
mFrameLoader->DestroyDocShell();
// In the out-of-process case, TabParent will eventually call
// DestroyComplete once it receives a __delete__ message from the child. In
// the in-process case, we dispatch a series of runnables to ensure that
// DestroyComplete gets called at the right time. The frame loader is kept
// alive by mFrameLoader during this time.
if (mFrameLoader->mChildMessageManager) {
// When the docshell is destroyed, NotifyWindowIDDestroyed is called to
// asynchronously notify {outer,inner}-window-destroyed via a runnable. We
// don't want DestroyComplete to run until after those runnables have
// run. Since we're enqueueing ourselves after the window-destroyed
// runnables are enqueued, we're guaranteed to run after.
mPhase = eWaitForUnloadMessage;
NS_DispatchToCurrentThread(this);
}
break;
case eWaitForUnloadMessage:
// The *-window-destroyed observers have finished running at this
// point. However, it's possible that a *-window-destroyed observer might
// have sent a message using the message manager. These messages might not
// have been processed yet. So we enqueue ourselves again to ensure that
// DestroyComplete runs after all messages sent by *-window-destroyed
// observers have been processed.
mPhase = eDestroyComplete;
NS_DispatchToCurrentThread(this);
break;
case eDestroyComplete:
// Now that all messages sent by unload listeners and window destroyed
// observers have been processed, we disconnect the message manager and
// finish destruction.
mFrameLoader->DestroyComplete();
break;
}
return NS_OK;
}
void
nsFrameLoader::DestroyDocShell()
{
// This code runs after the frameloader has been detached from the <browser>
// element. We postpone this work because we may not be allowed to run
// script at that time.
// Ask the TabChild to fire the frame script "unload" event, destroy its
// docshell, and finally destroy the PBrowser actor. This eventually leads to
// nsFrameLoader::DestroyComplete being called.
if (mRemoteBrowser) {
mRemoteBrowser->Destroy();
}
// Fire the "unload" event if we're in-process.
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->FireUnloadEvent();
}
// Destroy the docshell.
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (base_win) {
base_win->Destroy();
}
mDocShell = nullptr;
if (mChildMessageManager) {
// Stop handling events in the in-process frame script.
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->DisconnectEventListeners();
}
}
void
nsFrameLoader::DestroyComplete()
{
// We get here, as part of StartDestroy, after the docshell has been destroyed
// and all message manager messages sent during docshell destruction have been
// dispatched. We also get here if the child process crashes. In the latter
// case, StartDestroy might not have been called.
// Drop the strong references created in StartDestroy.
if (mChildMessageManager || mRemoteBrowser) {
mOwnerContentStrong = nullptr;
if (mRemoteBrowser) {
mRemoteBrowser->CacheFrameLoader(nullptr);
}
if (mChildMessageManager) {
mChildMessageManager->CacheFrameLoader(nullptr);
}
}
// Call TabParent::Destroy if we haven't already (in case of a crash).
if (mRemoteBrowser) {
mRemoteBrowser->SetOwnerElement(nullptr);
mRemoteBrowser->Destroy();
mRemoteBrowser = nullptr;
}
if (mMessageManager) {
mMessageManager->Disconnect();
}
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
}
mMessageManager = nullptr;
mChildMessageManager = nullptr;
}
NS_IMETHODIMP
nsFrameLoader::GetDepthTooGreat(bool* aDepthTooGreat)
{
*aDepthTooGreat = mDepthTooGreat;
return NS_OK;
}
void
nsFrameLoader::SetOwnerContent(Element* aContent)
{
if (mObservingOwnerContent) {
mObservingOwnerContent = false;
mOwnerContent->RemoveMutationObserver(this);
}
mOwnerContent = aContent;
if (RenderFrameParent* rfp = GetCurrentRenderFrame()) {
rfp->OwnerContentChanged(aContent);
}
ResetPermissionManagerStatus();
}
bool
nsFrameLoader::OwnerIsMozBrowserOrAppFrame()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false;
}
// The xpcom getter version
NS_IMETHODIMP
nsFrameLoader::GetOwnerIsMozBrowserOrAppFrame(bool* aResult)
{
*aResult = OwnerIsMozBrowserOrAppFrame();
return NS_OK;
}
bool
nsFrameLoader::OwnerIsWidget()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsWidget() : false;
}
// The xpcom getter version
NS_IMETHODIMP
nsFrameLoader::GetOwnerIsWidget(bool* aResult)
{
*aResult = OwnerIsWidget();
return NS_OK;
}
bool
nsFrameLoader::OwnerIsAppFrame()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsApp() : false;
}
bool
nsFrameLoader::OwnerIsMozBrowserFrame()
{
return OwnerIsMozBrowserOrAppFrame() && !OwnerIsAppFrame();
}
bool
nsFrameLoader::OwnerIsIsolatedMozBrowserFrame()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
if (!browserFrame) {
return false;
}
if (!OwnerIsMozBrowserFrame()) {
return false;
}
bool isolated = browserFrame->GetIsolated();
if (isolated) {
return true;
}
// After bug 1238160, which allows isolation to be disabled on mozbrowser
// frames, we no longer have a way to tell from the principal alone if
// something "is a mozbrowser". Instead, we now only know "is an isolated
// mozbrowser". The following code paths would return invalid results if it
// were possible to have apps *and* isolation could be disabled:
// * CheckPermission in AppProcessChecker.cpp
// * nsScriptSecurityManager::AppStatusForPrincipal
// * init() in SystemMessageManager.js
// Currently, desktop is the only platform where we intend to disable
// isolation on a browser frame, so non-desktop should be able to assume that
// inIsolatedMozBrowser is true for all mozbrowser frames. To enforce these
// assumptions, we assert that there are no apps installed if we have tried
// to disable isolation.
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
if (!appsService) {
// If the apps service is not present, we assume this means there can't be
// any apps at all, so there is no problem.
return false;
}
bool appsInstalled;
nsresult rv = appsService->AreAnyAppsInstalled(&appsInstalled);
if (NS_WARN_IF(NS_FAILED(rv))) {
// The apps service exists, but it threw an error when checking if there are
// any apps, so we don't know if we have them or not.
return false;
}
#ifdef MOZ_B2G
MOZ_RELEASE_ASSERT(!appsInstalled,
"Disabling mozbrowser isolation is not currently "
"allowed when apps are installed.");
#else
if (appsInstalled) {
NS_WARNING("Disabling mozbrowser isolation is not currently allowed when "
"apps are installed.");
}
#endif
return false;
}
void
nsFrameLoader::GetOwnerAppManifestURL(nsAString& aOut)
{
aOut.Truncate();
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
if (browserFrame) {
browserFrame->GetAppManifestURL(aOut);
}
}
already_AddRefed<mozIApplication>
nsFrameLoader::GetOwnApp()
{
nsAutoString manifest;
GetOwnerAppManifestURL(manifest);
if (manifest.IsEmpty()) {
return nullptr;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(appsService, nullptr);
nsCOMPtr<mozIApplication> app;
appsService->GetAppByManifestURL(manifest, getter_AddRefs(app));
return app.forget();
}
already_AddRefed<mozIApplication>
nsFrameLoader::GetContainingApp()
{
// See if our owner content's principal has an associated app.
uint32_t appId = mOwnerContent->NodePrincipal()->GetAppId();
MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
if (appId == nsIScriptSecurityManager::NO_APP_ID ||
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
return nullptr;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(appsService, nullptr);
nsCOMPtr<mozIApplication> app;
appsService->GetAppByLocalId(appId, getter_AddRefs(app));
return app.forget();
}
bool
nsFrameLoader::ShouldUseRemoteProcess()
{
if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
return false;
}
// Don't try to launch nested children if we don't have OMTC.
// They won't render!
if (XRE_IsContentProcess() &&
!CompositorBridgeChild::ChildProcessHasCompositorBridge()) {
return false;
}
if (XRE_IsContentProcess() &&
!(PR_GetEnv("MOZ_NESTED_OOP_TABS") ||
Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) {
return false;
}
// If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
// fall back to the default.
if (OwnerIsMozBrowserOrAppFrame() &&
!mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::Remote)) {
return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false);
}
// Otherwise, we're remote if we have "remote=true" and we're either a
// browser frame or a XUL element.
return (OwnerIsMozBrowserOrAppFrame() ||
mOwnerContent->GetNameSpaceID() == kNameSpaceID_XUL) &&
mOwnerContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::Remote,
nsGkAtoms::_true,
eCaseMatters);
}
bool
nsFrameLoader::IsRemoteFrame()
{
if (mRemoteFrame) {
MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell");
return true;
}
return false;
}
nsresult
nsFrameLoader::MaybeCreateDocShell()
{
if (mDocShell) {
return NS_OK;
}
if (IsRemoteFrame()) {
return NS_OK;
}
NS_ENSURE_STATE(!mDestroyCalled);
// Get our parent docshell off the document of mOwnerContent
// XXXbz this is such a total hack.... We really need to have a
// better setup for doing this.
nsIDocument* doc = mOwnerContent->OwnerDoc();
if (!(doc->IsStaticDocument() || mOwnerContent->IsInComposedDoc())) {
return NS_ERROR_UNEXPECTED;
}
if (doc->IsResourceDoc() || !doc->IsActive()) {
// Don't allow subframe loads in resource documents, nor
// in non-active documents.
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(docShell);
NS_ENSURE_STATE(parentAsWebNav);
// Create the docshell...
mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
if (mIsPrerendered) {
nsresult rv = mDocShell->SetIsPrerendered(true);
NS_ENSURE_SUCCESS(rv,rv);
}
// Apply sandbox flags even if our owner is not an iframe, as this copies
// flags from our owning content's owning document.
uint32_t sandboxFlags = 0;
HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
if (iframe) {
sandboxFlags = iframe->GetSandboxFlags();
}
ApplySandboxFlags(sandboxFlags);
if (!mNetworkCreated) {
if (mDocShell) {
mDocShell->SetCreatedDynamically(true);
}
}
// Get the frame name and tell the docshell about it.
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
nsAutoString frameName;
int32_t namespaceID = mOwnerContent->GetNameSpaceID();
if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
} else {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName);
// XXX if no NAME then use ID, after a transition period this will be
// changed so that XUL only uses ID too (bug 254284).
if (frameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
}
}
if (!frameName.IsEmpty()) {
mDocShell->SetName(frameName);
}
// Inform our docShell that it has a new child.
// Note: This logic duplicates a lot of logic in
// nsSubDocumentFrame::AttributeChanged. We should fix that.
int32_t parentType = docShell->ItemType();
// XXXbz why is this in content code, exactly? We should handle
// this some other way..... Not sure how yet.
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
NS_ENSURE_STATE(parentTreeOwner);
mIsTopLevelContent =
AddTreeItemToTreeOwner(mDocShell, parentTreeOwner, parentType, docShell);
// Make sure all shells have links back to the content element
// in the nearest enclosing chrome shell.
nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
if (parentType == nsIDocShellTreeItem::typeChrome) {
// Our parent shell is a chrome shell. It is therefore our nearest
// enclosing chrome shell.
chromeEventHandler = do_QueryInterface(mOwnerContent);
NS_ASSERTION(chromeEventHandler,
"This mContent should implement this.");
} else {
// Our parent shell is a content shell. Get the chrome event
// handler from it and use that for our shell as well.
docShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
}
mDocShell->SetChromeEventHandler(chromeEventHandler);
// This is nasty, this code (the mDocShell->GetWindow() below)
// *must* come *after* the above call to
// mDocShell->SetChromeEventHandler() for the global window to get
// the right chrome event handler.
// Tell the window about the frame that hosts it.
nsCOMPtr<Element> frame_element = mOwnerContent;
NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!");
nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (win_private) {
win_private->SetFrameElementInternal(frame_element);
}
// This is kinda whacky, this call doesn't really create anything,
// but it must be called to make sure things are properly
// initialized.
if (NS_FAILED(base_win->Create()) || !win_private) {
// Do not call Destroy() here. See bug 472312.
NS_WARNING("Something wrong when creating the docshell for a frameloader!");
return NS_ERROR_FAILURE;
}
if (mIsTopLevelContent &&
mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
!mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
nsresult rv;
nsCOMPtr<nsISHistory> sessionHistory =
do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
webNav->SetSessionHistory(sessionHistory);
}
DocShellOriginAttributes attrs;
if (!mOwnerContent->IsXULElement(nsGkAtoms::browser)) {
nsCOMPtr<nsIPrincipal> parentPrin = doc->NodePrincipal();
PrincipalOriginAttributes poa = BasePrincipal::Cast(parentPrin)->OriginAttributesRef();
attrs.InheritFromDocToChildDocShell(poa);
}
if (OwnerIsAppFrame()) {
// You can't be both an app and a browser frame.
MOZ_ASSERT(!OwnerIsMozBrowserFrame());
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
MOZ_ASSERT(ownApp);
uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
if (ownApp) {
NS_ENSURE_SUCCESS(ownApp->GetLocalId(&ownAppId), NS_ERROR_FAILURE);
}
attrs.mAppId = ownAppId;
mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_APP);
}
if (OwnerIsMozBrowserFrame()) {
// You can't be both a browser and an app frame.
MOZ_ASSERT(!OwnerIsAppFrame());
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
if (containingApp) {
NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
NS_ERROR_FAILURE);
}
attrs.mAppId = containingAppId;
attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
}
// Grab the userContextId from owner if XUL
nsresult rv = PopulateUserContextIdFromAttribute(attrs);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
if (OwnerIsMozBrowserOrAppFrame()) {
// For inproc frames, set the docshell properties.
nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
nsAutoString name;
if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
item->SetName(name);
}
mDocShell->SetFullscreenAllowed(
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen));
bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
if (isPrivate) {
bool nonBlank;
mDocShell->GetHasLoadedNonBlankURI(&nonBlank);
if (nonBlank) {
nsContentUtils::ReportToConsoleNonLocalized(
NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."),
nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("mozprivatebrowsing"),
nullptr);
} else {
nsCOMPtr<nsILoadContext> context = do_GetInterface(mDocShell);
context->SetUsePrivateBrowsing(true);
}
}
}
ReallyLoadFrameScripts();
InitializeBrowserAPI();
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"inprocess-browser-shown", nullptr);
}
return NS_OK;
}
void
nsFrameLoader::GetURL(nsString& aURI)
{
aURI.Truncate();
if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI);
} else {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI);
}
}
nsresult
nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
{
nsresult rv;
MOZ_ASSERT(!IsRemoteFrame(),
"Shouldn't call CheckForRecursiveLoad on remote frames.");
mDepthTooGreat = false;
rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
return NS_ERROR_FAILURE;
}
// Check that we're still in the docshell tree.
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
NS_WARN_IF_FALSE(treeOwner,
"Trying to load a new url to a docshell without owner!");
NS_ENSURE_STATE(treeOwner);
if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
// No need to do recursion-protection here XXXbz why not?? Do we really
// trust people not to screw up with non-content docshells?
return NS_OK;
}
// Bug 8065: Don't exceed some maximum depth in content frames
// (MAX_DEPTH_CONTENT_FRAMES)
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
int32_t depth = 0;
while (parentAsItem) {
++depth;
if (depth >= MAX_DEPTH_CONTENT_FRAMES) {
mDepthTooGreat = true;
NS_WARNING("Too many nested content frames so giving up");
return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?)
}
nsCOMPtr<nsIDocShellTreeItem> temp;
temp.swap(parentAsItem);
temp->GetSameTypeParent(getter_AddRefs(parentAsItem));
}
// Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
// srcdoc URIs require their contents to be specified inline, so it isn't
// possible for undesirable recursion to occur without the aid of a
// non-srcdoc URI, which this method will block normally.
// Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
nsAutoCString buffer;
rv = aURI->GetScheme(buffer);
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("about")) {
rv = aURI->GetPath(buffer);
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) {
// Duplicates allowed up to depth limits
return NS_OK;
}
}
int32_t matchCount = 0;
mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
while (parentAsItem) {
// Check the parent URI with the URI we're loading
nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentAsItem));
if (parentAsNav) {
// Does the URI match the one we're about to load?
nsCOMPtr<nsIURI> parentURI;
parentAsNav->GetCurrentURI(getter_AddRefs(parentURI));
if (parentURI) {
// Bug 98158/193011: We need to ignore data after the #
bool equal;
rv = aURI->EqualsExceptRef(parentURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
if (equal) {
matchCount++;
if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) {
NS_WARNING("Too many nested content frames have the same url (recursion?) so giving up");
return NS_ERROR_UNEXPECTED;
}
}
}
}
nsCOMPtr<nsIDocShellTreeItem> temp;
temp.swap(parentAsItem);
temp->GetSameTypeParent(getter_AddRefs(parentAsItem));
}
return NS_OK;
}
nsresult
nsFrameLoader::GetWindowDimensions(nsIntRect& aRect)
{
// Need to get outer window position here
nsIDocument* doc = mOwnerContent->GetComposedDoc();
if (!doc) {
return NS_ERROR_FAILURE;
}
if (doc->IsResourceDoc()) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
if (!win) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell());
if (!parentAsItem) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) ||
!parentOwner) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner));
treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y);
treeOwnerAsWin->GetSize(&aRect.width, &aRect.height);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
{
if (IsRemoteFrame()) {
if (mRemoteBrowser) {
ScreenIntSize size = aIFrame->GetSubdocumentSize();
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
return NS_OK;
}
UpdateBaseWindowPositionAndSize(aIFrame);
return NS_OK;
}
void
nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame)
{
nsCOMPtr<nsIDocShell> docShell;
GetDocShell(getter_AddRefs(docShell));
nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
// resize the sub document
if (baseWindow) {
int32_t x = 0;
int32_t y = 0;
nsWeakFrame weakFrame(aIFrame);
baseWindow->GetPosition(&x, &y);
if (!weakFrame.IsAlive()) {
// GetPosition() killed us
return;
}
ScreenIntSize size = aIFrame->GetSubdocumentSize();
baseWindow->SetPositionAndSize(x, y, size.width, size.height, false);
}
}
NS_IMETHODIMP
nsFrameLoader::GetEventMode(uint32_t* aEventMode)
{
*aEventMode = mEventMode;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetEventMode(uint32_t aEventMode)
{
mEventMode = aEventMode;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetClipSubdocument(bool* aResult)
{
*aResult = mClipSubdocument;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetClipSubdocument(bool aClip)
{
mClipSubdocument = aClip;
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
if (frame) {
frame->InvalidateFrame();
frame->PresContext()->PresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
if (subdocFrame) {
nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
if (subdocRootFrame) {
nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
GetRootScrollFrame();
if (subdocRootScrollFrame) {
frame->PresContext()->PresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetClampScrollPosition(bool* aResult)
{
*aResult = mClampScrollPosition;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetClampScrollPosition(bool aClamp)
{
mClampScrollPosition = aClamp;
// When turning clamping on, make sure the current position is clamped.
if (aClamp) {
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
if (subdocFrame) {
nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
if (subdocRootFrame) {
nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
GetRootScrollFrameAsScrollable();
if (subdocRootScrollFrame) {
subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT);
}
}
}
}
return NS_OK;
}
bool
nsFrameLoader::TryRemoteBrowser()
{
NS_ASSERTION(!mRemoteBrowser, "TryRemoteBrowser called with a remote browser already?");
//XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
// element isn't in document, only in shadow dom, but that will change
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
nsIDocument* doc = mOwnerContent->GetComposedDoc();
if (!doc) {
return false;
}
if (doc->IsResourceDoc()) {
// Don't allow subframe loads in external reference documents
return false;
}
nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow();
if (!parentWin) {
return false;
}
nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell();
if (!parentDocShell) {
return false;
}
TabParent* openingTab = TabParent::GetFrom(parentDocShell->GetOpener());
ContentParent* openerContentParent = nullptr;
if (openingTab &&
openingTab->Manager() &&
openingTab->Manager()->IsContentParent()) {
openerContentParent = openingTab->Manager()->AsContentParent();
}
// <iframe mozbrowser> gets to skip these checks.
if (!OwnerIsMozBrowserOrAppFrame()) {
if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
return false;
}
if (!mOwnerContent->IsXULElement()) {
return false;
}
nsAutoString value;
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, value);
if (!value.LowerCaseEqualsLiteral("content") &&
!StringBeginsWith(value, NS_LITERAL_STRING("content-"),
nsCaseInsensitiveStringComparator())) {
return false;
}
}
uint32_t chromeFlags = 0;
nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) ||
!parentOwner) {
return false;
}
nsCOMPtr<nsIXULWindow> window(do_GetInterface(parentOwner));
if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
return false;
}
PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser",
js::ProfileEntry::Category::OTHER);
MutableTabContext context;
nsresult rv = GetNewTabContext(&context);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<Element> ownerElement = mOwnerContent;
mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, openerContentParent);
if (!mRemoteBrowser) {
return false;
}
MaybeUpdatePrimaryTabParent(eTabParentChanged);
mChildID = mRemoteBrowser->Manager()->ChildID();
nsCOMPtr<nsIDocShellTreeItem> rootItem;
parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
if (rootChromeWin) {
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
}
if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::mozpasspointerevents,
nsGkAtoms::_true,
eCaseMatters)) {
Unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
}
ReallyLoadFrameScripts();
InitializeBrowserAPI();
return true;
}
mozilla::dom::PBrowserParent*
nsFrameLoader::GetRemoteBrowser() const
{
return mRemoteBrowser;
}
RenderFrameParent*
nsFrameLoader::GetCurrentRenderFrame() const
{
if (mRemoteBrowser) {
return mRemoteBrowser->GetRenderFrame();
}
return nullptr;
}
NS_IMETHODIMP
nsFrameLoader::ActivateRemoteFrame() {
if (mRemoteBrowser) {
mRemoteBrowser->Activate();
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsFrameLoader::DeactivateRemoteFrame() {
if (mRemoteBrowser) {
mRemoteBrowser->Deactivate();
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
}
void
nsFrameLoader::ActivateUpdateHitRegion() {
if (mRemoteBrowser) {
Unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
}
}
void
nsFrameLoader::DeactivateUpdateHitRegion() {
if (mRemoteBrowser) {
Unused << mRemoteBrowser->SendSetUpdateHitRegion(false);
}
}
NS_IMETHODIMP
nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType,
float aX,
float aY,
int32_t aButton,
int32_t aClickCount,
int32_t aModifiers,
bool aIgnoreRootScrollFrame)
{
if (mRemoteBrowser) {
mRemoteBrowser->SendMouseEvent(aType, aX, aY, aButton,
aClickCount, aModifiers,
aIgnoreRootScrollFrame);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsFrameLoader::ActivateFrameEvent(const nsAString& aType,
bool aCapture)
{
if (mRemoteBrowser) {
return mRemoteBrowser->SendActivateFrameEvent(nsString(aType), aCapture) ?
NS_OK : NS_ERROR_NOT_AVAILABLE;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsFrameLoader::SendCrossProcessKeyEvent(const nsAString& aType,
int32_t aKeyCode,
int32_t aCharCode,
int32_t aModifiers,
bool aPreventDefault)
{
if (mRemoteBrowser) {
mRemoteBrowser->SendKeyEvent(aType, aKeyCode, aCharCode, aModifiers,
aPreventDefault);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
nsresult
nsFrameLoader::CreateStaticClone(nsIFrameLoader* aDest)
{
nsFrameLoader* dest = static_cast<nsFrameLoader*>(aDest);
dest->MaybeCreateDocShell();
NS_ENSURE_STATE(dest->mDocShell);
nsCOMPtr<nsIDocument> dummy = dest->mDocShell->GetDocument();
nsCOMPtr<nsIContentViewer> viewer;
dest->mDocShell->GetContentViewer(getter_AddRefs(viewer));
NS_ENSURE_STATE(viewer);
nsCOMPtr<nsIDocShell> origDocShell;
GetDocShell(getter_AddRefs(origDocShell));
NS_ENSURE_STATE(origDocShell);
nsCOMPtr<nsIDocument> doc = origDocShell->GetDocument();
NS_ENSURE_STATE(doc);
nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(dest->mDocShell);
nsCOMPtr<nsIDOMDocument> clonedDOMDoc = do_QueryInterface(clonedDoc);
viewer->SetDOMDocument(clonedDOMDoc);
return NS_OK;
}
bool
nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
{
auto* tabParent = TabParent::GetFrom(GetRemoteBrowser());
if (tabParent) {
return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
}
RefPtr<nsInProcessTabChildGlobal> tabChild =
static_cast<nsInProcessTabChildGlobal*>(GetTabChildGlobalAsEventTarget());
if (tabChild) {
tabChild->LoadFrameScript(aURL, aRunInGlobalScope);
}
return true;
}
class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
public Runnable
{
public:
nsAsyncMessageToChild(JSContext* aCx, JS::Handle<JSObject*> aCpows, nsFrameLoader* aFrameLoader)
: nsSameProcessAsyncMessageBase(aCx, aCpows)
, mFrameLoader(aFrameLoader)
{
}
NS_IMETHOD Run()
{
nsInProcessTabChildGlobal* tabChild =
static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get());
// Since bug 1126089, messages can arrive even when the docShell is destroyed.
// Here we make sure that those messages are not delivered.
if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) {
nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(tabChild->GetGlobal());
ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader,
tabChild->GetInnerManager());
}
return NS_OK;
}
RefPtr<nsFrameLoader> mFrameLoader;
};
nsresult
nsFrameLoader::DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal)
{
TabParent* tabParent = mRemoteBrowser;
if (tabParent) {
ClonedMessageData data;
nsIContentParent* cp = tabParent->Manager();
if (!BuildClonedMessageDataForParent(cp, aData, data)) {
MOZ_CRASH();
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
jsipc::CPOWManager* mgr = cp->GetCPOWManager();
if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
return NS_ERROR_UNEXPECTED;
}
if (tabParent->SendAsyncMessage(nsString(aMessage), cpows,
IPC::Principal(aPrincipal), data)) {
return NS_OK;
} else {
return NS_ERROR_UNEXPECTED;
}
}
if (mChildMessageManager) {
RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(aCx, aCpows, this);
nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal);
if (NS_FAILED(rv)) {
return rv;
}
rv = NS_DispatchToCurrentThread(ev);
if (NS_FAILED(rv)) {
return rv;
}
return rv;
}
// We don't have any targets to send our asynchronous message to.
return NS_ERROR_UNEXPECTED;
}
bool
nsFrameLoader::CheckPermission(const nsAString& aPermission)
{
return AssertAppProcessPermission(GetRemoteBrowser(),
NS_ConvertUTF16toUTF8(aPermission).get());
}
bool
nsFrameLoader::CheckManifestURL(const nsAString& aManifestURL)
{
return AssertAppProcessManifestURL(GetRemoteBrowser(),
NS_ConvertUTF16toUTF8(aManifestURL).get());
}
bool
nsFrameLoader::CheckAppHasPermission(const nsAString& aPermission)
{
return AssertAppHasPermission(GetRemoteBrowser(),
NS_ConvertUTF16toUTF8(aPermission).get());
}
NS_IMETHODIMP
nsFrameLoader::GetMessageManager(nsIMessageSender** aManager)
{
EnsureMessageManager();
if (mMessageManager) {
RefPtr<nsFrameMessageManager> mm(mMessageManager);
mm.forget(aManager);
return NS_OK;
}
return NS_OK;
}
nsresult
nsFrameLoader::EnsureMessageManager()
{
NS_ENSURE_STATE(mOwnerContent);
if (mMessageManager) {
return NS_OK;
}
if (!mIsTopLevelContent &&
!OwnerIsMozBrowserOrAppFrame() &&
!IsRemoteFrame() &&
!(mOwnerContent->IsXULElement() &&
mOwnerContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::forcemessagemanager,
nsGkAtoms::_true, eCaseMatters))) {
return NS_OK;
}
nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
do_QueryInterface(GetOwnerDoc()->GetWindow());
nsCOMPtr<nsIMessageBroadcaster> parentManager;
if (chromeWindow) {
nsAutoString messagemanagergroup;
if (mOwnerContent->IsXULElement() &&
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::messagemanagergroup,
messagemanagergroup)) {
chromeWindow->GetGroupMessageManager(messagemanagergroup, getter_AddRefs(parentManager));
}
if (!parentManager) {
chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
}
} else {
parentManager = do_GetService("@mozilla.org/globalmessagemanager;1");
}
mMessageManager = new nsFrameMessageManager(nullptr,
static_cast<nsFrameMessageManager*>(parentManager.get()),
MM_CHROME);
if (!IsRemoteFrame()) {
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
return NS_ERROR_FAILURE;
}
mChildMessageManager =
new nsInProcessTabChildGlobal(mDocShell, mOwnerContent, mMessageManager);
}
return NS_OK;
}
nsresult
nsFrameLoader::ReallyLoadFrameScripts()
{
nsresult rv = EnsureMessageManager();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mMessageManager) {
mMessageManager->InitWithCallback(this);
}
return NS_OK;
}
EventTarget*
nsFrameLoader::GetTabChildGlobalAsEventTarget()
{
return static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
}
NS_IMETHODIMP
nsFrameLoader::GetOwnerElement(nsIDOMElement **aElement)
{
nsCOMPtr<nsIDOMElement> ownerElement = do_QueryInterface(mOwnerContent);
ownerElement.forget(aElement);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetChildID(uint64_t* aChildID)
{
*aChildID = mChildID;
return NS_OK;
}
void
nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
{
MOZ_ASSERT(!mRemoteBrowser);
mRemoteFrame = true;
mRemoteBrowser = TabParent::GetFrom(aTabParent);
mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0;
MaybeUpdatePrimaryTabParent(eTabParentChanged);
ReallyLoadFrameScripts();
InitializeBrowserAPI();
ShowRemoteFrame(ScreenIntSize(0, 0));
}
nsresult
nsFrameLoader::SwapRemoteBrowser(nsITabParent* aTabParent)
{
RefPtr<TabParent> newParent = TabParent::GetFrom(aTabParent);
if (!newParent || !mRemoteBrowser) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (!IsRemoteFrame()) {
NS_WARNING("Switching from in-process to out-of-process is not supported.");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (!OwnerIsMozBrowserOrAppFrame()) {
NS_WARNING("Switching process for non-mozbrowser/app frame is not supported.");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (newParent == mRemoteBrowser) {
return NS_OK;
}
MaybeUpdatePrimaryTabParent(eTabParentRemoved);
mRemoteBrowser->CacheFrameLoader(nullptr);
mRemoteBrowser->SetOwnerElement(nullptr);
mRemoteBrowser->Detach();
mRemoteBrowser->Destroy();
mRemoteBrowser = newParent;
mRemoteBrowser->Attach(this);
mChildID = mRemoteBrowser->Manager()->ChildID();
MaybeUpdatePrimaryTabParent(eTabParentChanged);
// Force the new remote frame manager to load pending scripts
mMessageManager->LoadPendingScripts();
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"remote-browser-swapped", nullptr);
}
if (!mRemoteBrowserShown) {
ShowRemoteFrame(ScreenIntSize(0, 0));
}
return NS_OK;
}
void
nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame,
nsIDocument* aContainerDoc)
{
mDetachedSubdocFrame = aDetachedFrame;
mContainerDocWhileDetached = aContainerDoc;
}
nsIFrame*
nsFrameLoader::GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const
{
NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached);
return mDetachedSubdocFrame.GetFrame();
}
void
nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags)
{
if (mDocShell) {
uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
// The child can only add restrictions, never remove them.
sandboxFlags |= parentSandboxFlags;
mDocShell->SetSandboxFlags(sandboxFlags);
}
}
/* virtual */ void
nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
MOZ_ASSERT(mObservingOwnerContent);
if (aNameSpaceID != kNameSpaceID_None || aAttribute != TypeAttrName()) {
return;
}
if (aElement != mOwnerContent) {
return;
}
// Note: This logic duplicates a lot of logic in
// MaybeCreateDocshell. We should fix that.
// Notify our enclosing chrome that our type has changed. We only do this
// if our parent is chrome, since in all other cases we're random content
// subframes and the treeowner shouldn't worry about us.
if (!mDocShell) {
MaybeUpdatePrimaryTabParent(eTabParentChanged);
return;
}
nsCOMPtr<nsIDocShellTreeItem> parentItem;
mDocShell->GetParent(getter_AddRefs(parentItem));
if (!parentItem) {
return;
}
if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) {
return;
}
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
if (!parentTreeOwner) {
return;
}
nsAutoString value;
aElement->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
#ifdef MOZ_XUL
// when a content panel is no longer primary, hide any open popups it may have
if (!is_primary) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->HidePopupsInDocShell(mDocShell);
}
#endif
parentTreeOwner->ContentShellRemoved(mDocShell);
if (value.LowerCaseEqualsLiteral("content") ||
StringBeginsWith(value, NS_LITERAL_STRING("content-"),
nsCaseInsensitiveStringComparator())) {
bool is_targetable = is_primary ||
value.LowerCaseEqualsLiteral("content-targetable");
parentTreeOwner->ContentShellAdded(mDocShell, is_primary,
is_targetable, value);
}
}
void
nsFrameLoader::ResetPermissionManagerStatus()
{
// The resetting of the permissions status can run only
// in the main process.
// only in-main-process && in-process frame is handled here and all other
// cases are handled by ContentParent.
if (XRE_IsContentProcess() || mRemoteFrame) {
return;
}
// Finding the new app Id:
// . first we check if the owner is an app frame
// . second, we check if the owner is a browser frame
// in both cases we populate the appId variable.
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
if (OwnerIsAppFrame()) {
// You can't be both an app and a browser frame.
MOZ_ASSERT(!OwnerIsMozBrowserFrame());
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
MOZ_ASSERT(ownApp);
uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
if (ownApp && NS_SUCCEEDED(ownApp->GetLocalId(&ownAppId))) {
appId = ownAppId;
}
}
if (OwnerIsMozBrowserFrame()) {
// You can't be both a browser and an app frame.
MOZ_ASSERT(!OwnerIsAppFrame());
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
if (containingApp && NS_SUCCEEDED(containingApp->GetLocalId(&containingAppId))) {
appId = containingAppId;
}
}
// Nothing changed.
if (appId == mAppIdSentToPermissionManager) {
return;
}
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
NS_ERROR("No PermissionManager available!");
return;
}
// If previously we registered an appId, we have to unregister it.
if (mAppIdSentToPermissionManager != nsIScriptSecurityManager::NO_APP_ID) {
permMgr->ReleaseAppId(mAppIdSentToPermissionManager);
mAppIdSentToPermissionManager = nsIScriptSecurityManager::NO_APP_ID;
}
// Register the new AppId.
if (appId != nsIScriptSecurityManager::NO_APP_ID) {
mAppIdSentToPermissionManager = appId;
permMgr->AddrefAppId(mAppIdSentToPermissionManager);
}
}
/**
* Send the RequestNotifyAfterRemotePaint message to the current Tab.
*/
NS_IMETHODIMP
nsFrameLoader::RequestNotifyAfterRemotePaint()
{
// If remote browsing (e10s), handle this with the TabParent.
if (mRemoteBrowser) {
Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
}
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeReady()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeReady() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
if (!mOwnerContent) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeReady"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeCleared()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeCleared() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
if (!mOwnerContent) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeCleared"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
/* [infallible] */ NS_IMETHODIMP
nsFrameLoader::SetVisible(bool aVisible)
{
if (mVisible == aVisible) {
return NS_OK;
}
mVisible = aVisible;
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"frameloader-visible-changed", nullptr);
}
return NS_OK;
}
/* [infallible] */ NS_IMETHODIMP
nsFrameLoader::GetVisible(bool* aVisible)
{
*aVisible = mVisible;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetTabParent(nsITabParent** aTabParent)
{
nsCOMPtr<nsITabParent> tp = mRemoteBrowser;
tp.forget(aTabParent);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetLoadContext(nsILoadContext** aLoadContext)
{
nsCOMPtr<nsILoadContext> loadContext;
if (mRemoteBrowser) {
loadContext = mRemoteBrowser->GetLoadContext();
} else {
nsCOMPtr<nsIDocShell> docShell;
GetDocShell(getter_AddRefs(docShell));
loadContext = do_GetInterface(docShell);
}
loadContext.forget(aLoadContext);
return NS_OK;
}
void
nsFrameLoader::InitializeBrowserAPI()
{
if (OwnerIsMozBrowserOrAppFrame()) {
if (!IsRemoteFrame()) {
nsresult rv = EnsureMessageManager();
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (mMessageManager) {
mMessageManager->LoadFrameScript(
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
/* allowDelayedLoad = */ true,
/* aRunInGlobalScope */ true);
}
}
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
if (browserFrame) {
browserFrame->InitializeBrowserAPI();
}
}
}
NS_IMETHODIMP
nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
nsIWebBrowserPersistDocumentReceiver* aRecv)
{
if (!aRecv) {
return NS_ERROR_INVALID_POINTER;
}
if (mRemoteBrowser) {
return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv);
}
nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(mDocShell);
nsCOMPtr<nsIDocument> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
} else {
foundDoc = rootDoc;
}
if (!foundDoc) {
aRecv->OnError(NS_ERROR_NO_CONTENT);
} else {
nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
new mozilla::WebBrowserPersistLocalDocument(foundDoc);
aRecv->OnDocumentReady(pdoc);
}
return NS_OK;
}
void
nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange)
{
if (mRemoteBrowser && mOwnerContent) {
nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
if (!docShell) {
return;
}
int32_t parentType = docShell->ItemType();
if (parentType != nsIDocShellTreeItem::typeChrome) {
return;
}
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
if (!parentTreeOwner) {
return;
}
if (!mObservingOwnerContent) {
mOwnerContent->AddMutationObserver(this);
mObservingOwnerContent = true;
}
parentTreeOwner->TabParentRemoved(mRemoteBrowser);
if (aChange == eTabParentChanged) {
bool isPrimary =
mOwnerContent->AttrValueIs(kNameSpaceID_None,
TypeAttrName(),
NS_LITERAL_STRING("content-primary"),
eIgnoreCase);
parentTreeOwner->TabParentAdded(mRemoteBrowser, isPrimary);
}
}
}
nsresult
nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
nsIURI* aURI,
const nsACString& aPackageId)
{
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
DocShellOriginAttributes attrs;
attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
nsCString signedPkgOrigin;
if (!aPackageId.IsEmpty()) {
// Only when aPackageId is not empty would signed package origin
// be meaningful.
nsPrincipal::GetOriginForURI(aURI, signedPkgOrigin);
}
// Get the AppId from ownApp
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
if (ownApp) {
nsresult rv = ownApp->GetLocalId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID);
} else if (containingApp) {
nsresult rv = containingApp->GetLocalId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID);
}
attrs.mAppId = appId;
// Populate packageId to signedPkg.
attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId);
// set the userContextId on the attrs before we pass them into
// the tab context
nsAutoString userContextIdStr;
if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::usercontextid,
userContextIdStr);
}
if (!userContextIdStr.IsEmpty()) {
nsresult err;
uint32_t userContextId = userContextIdStr.ToInteger(&err);
NS_ENSURE_SUCCESS(err, err);
attrs.mUserContextId = userContextId;
}
bool tabContextUpdated =
aTabContext->SetTabContext(OwnerIsMozBrowserFrame(),
ownApp,
containingApp,
attrs,
signedPkgOrigin);
NS_ENSURE_STATE(tabContextUpdated);
return NS_OK;
}
nsresult
nsFrameLoader::PopulateUserContextIdFromAttribute(DocShellOriginAttributes& aAttr)
{
if (aAttr.mUserContextId ==
nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
// Grab the userContextId from owner if XUL
nsAutoString userContextIdStr;
int32_t namespaceID = mOwnerContent->GetNameSpaceID();
if ((namespaceID == kNameSpaceID_XUL) &&
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid,
userContextIdStr) &&
!userContextIdStr.IsEmpty()) {
nsresult rv;
aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}