Bug 567058 - Stop using intr messages for window.open (r=bent)

This commit is contained in:
Bill McCloskey 2015-01-12 20:36:42 -08:00
parent bc4b2a086c
commit 294da091dd
7 changed files with 237 additions and 120 deletions

View File

@ -2305,7 +2305,7 @@ nsFrameLoader::CreateStaticClone(nsIFrameLoader* aDest)
bool
nsFrameLoader::DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
{
mozilla::dom::PBrowserParent* tabParent = GetRemoteBrowser();
auto* tabParent = static_cast<TabParent*>(GetRemoteBrowser());
if (tabParent) {
return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
}

View File

@ -994,6 +994,11 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
return nullptr;
}
if (TabParent* parent = TabParent::GetNextTabParent()) {
parent->SetOwnerElement(aFrameElement);
return parent;
}
ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
bool isInContentProcess = (XRE_GetProcessType() != GeckoProcessType_Default);
TabId tabId;

View File

@ -79,6 +79,12 @@ struct ShowInfo
double defaultScale;
};
struct FrameScriptInfo
{
nsString url;
bool runInGlobalScope;
};
prio(normal upto urgent) intr protocol PBrowser
{
manager PContent or PContentBridge;
@ -117,7 +123,8 @@ parent:
Event(RemoteDOMEvent aEvent);
intr CreateWindow(uint32_t aChromeFlags,
sync CreateWindow(PBrowser aNewTab,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified,
@ -125,7 +132,7 @@ parent:
nsString aName,
nsString aFeatures,
nsString aBaseURI)
returns (bool windowIsNew, PBrowser window);
returns (bool windowOpened, FrameScriptInfo[] frameScripts);
sync SyncMessage(nsString aMessage, ClonedMessageData aData,
CpowEntry[] aCpows, Principal aPrincipal)

View File

@ -1429,96 +1429,44 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags,
// isn't a request to open a modal-type window, we're going to create a new
// <iframe mozbrowser/mozapp> and return its window here.
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
if (docshell && docshell->GetIsInBrowserOrApp() &&
!(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
bool iframeMoz = (docshell && docshell->GetIsInBrowserOrApp() &&
!(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
nsIWebBrowserChrome::CHROME_OPENAS_CHROME)));
// Note that BrowserFrameProvideWindow may return NS_ERROR_ABORT if the
// open window call was canceled. It's important that we pass this error
// code back to our caller.
return ProvideWindowCommon(aParent,
aChromeFlags,
aCalledFromJS,
aPositionSpecified,
aSizeSpecified,
aURI,
aName,
aFeatures,
aWindowIsNew,
aReturn);
if (!iframeMoz) {
int32_t openLocation =
nsWindowWatcher::GetWindowOpenLocation(aParent, aChromeFlags, aCalledFromJS,
aPositionSpecified, aSizeSpecified);
// If it turns out we're opening in the current browser, just hand over the
// current browser's docshell.
if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
*aWindowIsNew = false;
return browser->GetContentDOMWindow(aReturn);
}
}
int32_t openLocation =
nsWindowWatcher::GetWindowOpenLocation(aParent, aChromeFlags, aCalledFromJS,
aPositionSpecified, aSizeSpecified);
// If it turns out we're opening in the current browser, just hand over the
// current browser's docshell.
if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
*aWindowIsNew = false;
return browser->GetContentDOMWindow(aReturn);
}
// Otherwise, we're opening a new tab or a new window. We have to contact
// TabParent in order to do either.
PBrowserChild* newChild;
nsAutoCString uriString;
if (aURI) {
aURI->GetSpec(uriString);
}
nsCOMPtr<nsIDOMDocument> domDoc;
aParent->GetDocument(getter_AddRefs(domDoc));
if (!domDoc) {
NS_ERROR("Could retrieve document from nsIBaseWindow");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc;
doc = do_QueryInterface(domDoc);
if (!doc) {
NS_ERROR("Document from nsIBaseWindow didn't QI to nsIDocument");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
if (!baseURI) {
NS_ERROR("nsIDocument didn't return a base URI");
return NS_ERROR_FAILURE;
}
nsAutoCString baseURIString;
baseURI->GetSpec(baseURIString);
nsAutoString nameString;
nameString.Assign(aName);
nsAutoCString features;
// We can assume that if content is requesting to open a window from a remote
// tab, then we want to enforce that the new window is also a remote tab.
features.Assign(aFeatures);
features.Append(",remote");
if (!CallCreateWindow(aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified, NS_ConvertUTF8toUTF16(uriString),
nameString, NS_ConvertUTF8toUTF16(features),
NS_ConvertUTF8toUTF16(baseURIString),
aWindowIsNew, &newChild)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIDOMWindow> win =
do_GetInterface(static_cast<TabChild*>(newChild)->WebNavigation());
win.forget(aReturn);
return NS_OK;
// Note that ProvideWindowCommon may return NS_ERROR_ABORT if the
// open window call was canceled. It's important that we pass this error
// code back to our caller.
return ProvideWindowCommon(aParent,
iframeMoz,
aChromeFlags,
aCalledFromJS,
aPositionSpecified,
aSizeSpecified,
aURI,
aName,
aFeatures,
aWindowIsNew,
aReturn);
}
nsresult
TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
bool aIframeMoz,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
@ -1570,9 +1518,51 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
NS_ConvertUTF8toUTF16 url(spec);
nsString name(aName);
NS_ConvertUTF8toUTF16 features(aFeatures);
newChild->SendBrowserFrameOpenWindow(this, url, name,
features, aWindowIsNew);
nsAutoCString features(aFeatures);
nsTArray<FrameScriptInfo> frameScripts;
if (aIframeMoz) {
newChild->SendBrowserFrameOpenWindow(this, url, name,
NS_ConvertUTF8toUTF16(features),
aWindowIsNew);
} else {
nsCOMPtr<nsIDOMDocument> domDoc;
aOpener->GetDocument(getter_AddRefs(domDoc));
if (!domDoc) {
NS_ERROR("Could retrieve document from nsIBaseWindow");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc;
doc = do_QueryInterface(domDoc);
if (!doc) {
NS_ERROR("Document from nsIBaseWindow didn't QI to nsIDocument");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
if (!baseURI) {
NS_ERROR("nsIDocument didn't return a base URI");
return NS_ERROR_FAILURE;
}
nsAutoCString baseURIString;
baseURI->GetSpec(baseURIString);
// We can assume that if content is requesting to open a window from a remote
// tab, then we want to enforce that the new window is also a remote tab.
features.AppendLiteral(",remote");
if (!SendCreateWindow(newChild,
aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified, url,
name, NS_ConvertUTF8toUTF16(features),
NS_ConvertUTF8toUTF16(baseURIString),
aWindowIsNew,
&frameScripts)) {
return NS_ERROR_NOT_AVAILABLE;
}
}
if (!*aWindowIsNew) {
PBrowserChild::Send__delete__(newChild);
return NS_ERROR_ABORT;
@ -1595,6 +1585,13 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
// pretty bogus; see bug 763602.
newChild->DoFakeShow(scrolling, textureFactoryIdentifier, layersId, renderFrame);
for (size_t i = 0; i < frameScripts.Length(); i++) {
FrameScriptInfo& info = frameScripts[i];
if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
MOZ_CRASH();
}
}
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->WebNavigation());
win.forget(aReturn);
return NS_OK;
@ -1926,7 +1923,14 @@ TabChild::ApplyShowInfo(const ShowInfo& aInfo)
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {
nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
item->SetName(aInfo.name());
if (IsBrowserOrApp()) {
// B2G allows window.name to be set by changing the name attribute on the
// <iframe mozbrowser> element. window.open calls cause this attribute to
// be set to the correct value. A normal <xul:browser> element has no such
// attribute. The data we get here comes from reading the attribute, so we
// shouldn't trust it for <xul:browser> elements.
item->SetName(aInfo.name());
}
docShell->SetFullscreenAllowed(aInfo.fullscreenAllowed());
if (aInfo.isPrivate()) {
bool nonBlank;

View File

@ -575,6 +575,7 @@ private:
nsresult
ProvideWindowCommon(nsIDOMWindow* aOpener,
bool aIframeMoz,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,

View File

@ -272,6 +272,7 @@ TabParent::TabParent(nsIContentParent* aManager,
, mChromeFlags(aChromeFlags)
, mInitedByParent(false)
, mTabId(aTabId)
, mSkipLoad(false)
{
MOZ_ASSERT(aManager);
}
@ -449,18 +450,49 @@ TabParent::RecvEvent(const RemoteDOMEvent& aEvent)
return true;
}
bool
TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
const bool& aCalledFromJS,
const bool& aPositionSpecified,
const bool& aSizeSpecified,
const nsString& aURI,
const nsString& aName,
const nsString& aFeatures,
const nsString& aBaseURI,
bool* aWindowIsNew,
PBrowserParent** aRetVal)
class MOZ_STACK_CLASS TabParent::AutoUseNewTab MOZ_FINAL
{
public:
AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew)
: mNewTab(aNewTab), mWindowIsNew(aWindowIsNew)
{
MOZ_ASSERT(!TabParent::sNextTabParent);
TabParent::sNextTabParent = aNewTab;
aNewTab->mSkipLoad = true;
}
~AutoUseNewTab()
{
mNewTab->mSkipLoad = false;
if (TabParent::sNextTabParent) {
MOZ_ASSERT(TabParent::sNextTabParent == mNewTab);
TabParent::sNextTabParent = nullptr;
*mWindowIsNew = false;
}
}
private:
TabParent* mNewTab;
bool* mWindowIsNew;
};
bool
TabParent::RecvCreateWindow(PBrowserParent* aNewTab,
const uint32_t& aChromeFlags,
const bool& aCalledFromJS,
const bool& aPositionSpecified,
const bool& aSizeSpecified,
const nsString& aURI,
const nsString& aName,
const nsString& aFeatures,
const nsString& aBaseURI,
bool* aWindowIsNew,
InfallibleTArray<FrameScriptInfo>* aFrameScripts)
{
// We always expect to open a new window here. If we don't, it's an error.
*aWindowIsNew = true;
if (IsBrowserOrApp()) {
return false;
}
@ -470,6 +502,8 @@ TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, false);
TabParent* newTab = static_cast<TabParent*>(aNewTab);
nsCOMPtr<nsIContent> frame(do_QueryInterface(mFrameElement));
NS_ENSURE_TRUE(frame, false);
@ -483,8 +517,6 @@ TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
*aWindowIsNew = true;
// Opening new tabs is the easy case...
if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
NS_ENSURE_TRUE(mBrowserDOMWindow, false);
@ -497,17 +529,18 @@ TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
params->SetReferrer(aBaseURI);
params->SetIsPrivate(isPrivate);
AutoUseNewTab aunt(newTab, aWindowIsNew);
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
mBrowserDOMWindow->OpenURIInFrame(nullptr, params,
nsIBrowserDOMWindow::OPEN_NEWTAB,
openLocation,
nsIBrowserDOMWindow::OPEN_NEW,
getter_AddRefs(frameLoaderOwner));
NS_ENSURE_TRUE(frameLoaderOwner, false);
if (!frameLoaderOwner) {
*aWindowIsNew = false;
}
nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
NS_ENSURE_TRUE(frameLoader, false);
*aRetVal = frameLoader->GetRemoteBrowser();
aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
return true;
}
@ -530,6 +563,8 @@ TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
nsCOMPtr<nsIDOMWindow> window;
AutoUseNewTab aunt(newTab, aWindowIsNew);
rv = pwwatch->OpenWindow2(parent, finalURIString.get(),
NS_ConvertUTF16toUTF8(aName).get(),
NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS,
@ -545,15 +580,45 @@ TabParent::AnswerCreateWindow(const uint32_t& aChromeFlags,
nsCOMPtr<nsITabParent> newRemoteTab = newDocShell->GetOpenedRemote();
NS_ENSURE_TRUE(newRemoteTab, false);
*aRetVal = static_cast<TabParent*>(newRemoteTab.get());
MOZ_ASSERT(static_cast<TabParent*>(newRemoteTab.get()) == newTab);
aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
return true;
}
TabParent* TabParent::sNextTabParent;
/* static */ TabParent*
TabParent::GetNextTabParent()
{
TabParent* result = sNextTabParent;
sNextTabParent = nullptr;
return result;
}
bool
TabParent::SendLoadRemoteScript(const nsString& aURL,
const bool& aRunInGlobalScope)
{
if (mSkipLoad) {
mDelayedFrameScripts.AppendElement(FrameScriptInfo(aURL, aRunInGlobalScope));
return true;
}
MOZ_ASSERT(mDelayedFrameScripts.IsEmpty());
return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope);
}
void
TabParent::LoadURL(nsIURI* aURI)
{
MOZ_ASSERT(aURI);
if (mSkipLoad) {
// Don't send the message if the child wants to load its own URL.
return;
}
if (mIsDestroyed) {
return;
}

View File

@ -134,16 +134,17 @@ public:
const nsString& aName,
const nsString& aFeatures,
bool* aOutWindowOpened) MOZ_OVERRIDE;
virtual bool AnswerCreateWindow(const uint32_t& aChromeFlags,
const bool& aCalledFromJS,
const bool& aPositionSpecified,
const bool& aSizeSpecified,
const nsString& aURI,
const nsString& aName,
const nsString& aFeatures,
const nsString& aBaseURI,
bool* aWindowIsNew,
PBrowserParent** aRetVal) MOZ_OVERRIDE;
virtual bool RecvCreateWindow(PBrowserParent* aOpener,
const uint32_t& aChromeFlags,
const bool& aCalledFromJS,
const bool& aPositionSpecified,
const bool& aSizeSpecified,
const nsString& aURI,
const nsString& aName,
const nsString& aFeatures,
const nsString& aBaseURI,
bool* aWindowIsNew,
InfallibleTArray<FrameScriptInfo>* aFrameScripts) MOZ_OVERRIDE;
virtual bool RecvSyncMessage(const nsString& aMessage,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
@ -352,6 +353,11 @@ public:
void SetInitedByParent() { mInitedByParent = true; }
bool IsInitedByParent() const { return mInitedByParent; }
static TabParent* GetNextTabParent();
bool SendLoadRemoteScript(const nsString& aURL,
const bool& aRunInGlobalScope);
protected:
bool ReceiveMessage(const nsString& aMessage,
bool aSync,
@ -468,6 +474,35 @@ private:
TabId mTabId;
// Helper class for RecvCreateWindow.
struct AutoUseNewTab;
// When loading a new tab or window via window.open, the child process sends
// a new PBrowser to use. We store that tab in sNextTabParent and then
// proceed through the browser's normal paths to create a new
// window/tab. When it comes time to create a new TabParent, we instead use
// sNextTabParent.
static TabParent* sNextTabParent;
// When loading a new tab or window via window.open, the child is
// responsible for loading the URL it wants into the new
// TabChild. Simultaneously, though, the parent sends a LoadURL message to
// every new PBrowser (usually for about:blank). This message usually
// arrives after the child has started to load the URL it wants, and
// overrides it. To prevent this, we set mSkipLoad to true when creating the
// new tab. This flag prevents the unwanted LoadURL message from being sent
// by the parent.
bool mSkipLoad;
// When loading a new tab or window via window.open, we want to ensure that
// frame scripts for that tab are loaded before any scripts start to run in
// the window. We can't load the frame scripts the normal way, using
// separate IPC messages, since they won't be processed by the child until
// returning to the event loop, which is too late. Instead, we queue up
// frame scripts that we intend to load and send them as part of the
// CreateWindow response. Then TabChild loads them immediately.
nsTArray<FrameScriptInfo> mDelayedFrameScripts;
private:
// This is used when APZ needs to find the TabParent associated with a layer
// to dispatch events.