Bug 719459 - Add onmozbrowsertitlechange event. r=smaug

--HG--
extra : rebase_source : b5e8ee64c85e0090a38a849bf077a68055ff56b9
This commit is contained in:
Justin Lebar 2012-01-26 18:03:22 -05:00
Родитель 6b2098fe3a
Коммит da0cb52496
8 изменённых файлов: 323 добавлений и 30 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include "nsIDOMCustomEvent.h"
#include "nsIVariant.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsWeakPtr.h"
#include "nsVariant.h"
#include "nsContentUtils.h"
#include "nsDOMMemoryReporter.h"
@ -37,10 +38,13 @@ NS_INTERFACE_TABLE_HEAD(nsGenericHTMLFrameElement)
NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
NS_IMPL_INT_ATTR(nsGenericHTMLFrameElement, TabIndex, tabindex)
NS_IMPL_BOOL_ATTR(nsGenericHTMLFrameElement, Mozbrowser, mozbrowser)
nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
{
if (mTitleChangedListener) {
mTitleChangedListener->Unregister();
}
if (mFrameLoader) {
mFrameLoader->Destroy();
}
@ -112,17 +116,7 @@ nsGenericHTMLFrameElement::EnsureFrameLoader()
return NS_OK;
}
// Register ourselves as a web progress listener on the frameloader's
// docshell.
nsCOMPtr<nsIDocShell> docShell;
mFrameLoader->GetDocShell(getter_AddRefs(docShell));
nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(docShell);
NS_ENSURE_TRUE(webProgress, NS_OK);
// This adds a weak ref, so we don't have to worry about unregistering.
webProgress->AddProgressListener(this,
nsIWebProgress::NOTIFY_LOCATION |
nsIWebProgress::NOTIFY_STATE_WINDOW);
MaybeEnsureBrowserFrameListenersRegistered();
return NS_OK;
}
@ -285,6 +279,84 @@ nsGenericHTMLFrameElement::SizeOf() const
return size;
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::GetMozbrowser(bool *aValue)
{
return GetBoolAttr(nsGkAtoms::mozbrowser, aValue);
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::SetMozbrowser(bool aValue)
{
nsresult rv = SetBoolAttr(nsGkAtoms::mozbrowser, aValue);
if (NS_SUCCEEDED(rv)) {
MaybeEnsureBrowserFrameListenersRegistered();
}
return rv;
}
/*
* If this frame element is allowed to be a browser frame (because it passes
* BrowserFrameSecurityCheck()), then make sure that it has the appropriate
* event listeners enabled.
*/
void
nsGenericHTMLFrameElement::MaybeEnsureBrowserFrameListenersRegistered()
{
if (mBrowserFrameListenersRegistered) {
return;
}
// If this frame passes the browser frame security check, ensure that its
// listeners are active.
if (!BrowserFrameSecurityCheck()) {
return;
}
// Not much we can do without a frameLoader. But EnsureFrameLoader will call
// this function, so we'll get a chance to pass this test.
if (!mFrameLoader) {
return;
}
mBrowserFrameListenersRegistered = true;
// Register ourselves as a web progress listener on the frameloader's
// docshell.
nsCOMPtr<nsIDocShell> docShell;
mFrameLoader->GetDocShell(getter_AddRefs(docShell));
nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(docShell);
// This adds a weak ref, so we don't have to worry about unregistering.
if (webProgress) {
webProgress->AddProgressListener(this,
nsIWebProgress::NOTIFY_LOCATION |
nsIWebProgress::NOTIFY_STATE_WINDOW);
}
// Register a listener for DOMTitleChanged on the window's chrome event
// handler. The chrome event handler outlives this iframe, so we'll have to
// unregister when the iframe is destroyed.
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
if (!window) {
return;
}
MOZ_ASSERT(window->IsOuterWindow());
nsIDOMEventTarget *chromeHandler = window->GetChromeEventHandler();
if (!chromeHandler) {
return;
}
MOZ_ASSERT(!mTitleChangedListener);
mTitleChangedListener = new TitleChangedListener(this, chromeHandler);
chromeHandler->AddSystemEventListener(NS_LITERAL_STRING("DOMTitleChanged"),
mTitleChangedListener,
/* useCapture = */ false,
/* wantsUntrusted = */ false);
}
/**
* Return true if this frame element has permission to send mozbrowser
* events, and false otherwise.
@ -449,3 +521,77 @@ nsGenericHTMLFrameElement::OnSecurityChange(nsIWebProgress *aWebProgress,
{
return NS_OK;
}
NS_IMPL_ISUPPORTS1(nsGenericHTMLFrameElement::TitleChangedListener,
nsIDOMEventListener)
nsGenericHTMLFrameElement::TitleChangedListener::TitleChangedListener(
nsGenericHTMLFrameElement *aElement,
nsIDOMEventTarget *aChromeHandler)
{
mElement =
do_GetWeakReference(NS_ISUPPORTS_CAST(nsIDOMMozBrowserFrame*, aElement));
mChromeHandler = do_GetWeakReference(aChromeHandler);
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::TitleChangedListener::HandleEvent(nsIDOMEvent *aEvent)
{
#ifdef DEBUG
{
nsString eventType;
aEvent->GetType(eventType);
MOZ_ASSERT(eventType.EqualsLiteral("DOMTitleChanged"));
}
#endif
nsCOMPtr<nsIDOMMozBrowserFrame> element = do_QueryReferent(mElement);
if (!element) {
// Hm, our element is gone, but somehow we weren't unregistered?
Unregister();
return NS_OK;
}
nsGenericHTMLFrameElement* frameElement =
static_cast<nsGenericHTMLFrameElement*>(element.get());
nsCOMPtr<nsIDOMDocument> frameDocument;
frameElement->GetContentDocument(getter_AddRefs(frameDocument));
NS_ENSURE_STATE(frameDocument);
nsCOMPtr<nsIDOMEventTarget> target;
aEvent->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIDOMDocument> targetDocument = do_QueryInterface(target);
NS_ENSURE_STATE(targetDocument);
if (frameDocument != targetDocument) {
// This is a titlechange event for the wrong document!
return NS_OK;
}
nsString newTitle;
nsresult rv = targetDocument->GetTitle(newTitle);
NS_ENSURE_SUCCESS(rv, rv);
frameElement->MaybeFireBrowserEvent(
NS_LITERAL_STRING("titlechange"),
NS_LITERAL_STRING("customevent"),
newTitle);
return NS_OK;
}
void
nsGenericHTMLFrameElement::TitleChangedListener::Unregister()
{
nsCOMPtr<nsIDOMEventTarget> chromeHandler = do_QueryReferent(mChromeHandler);
if (!chromeHandler) {
return;
}
chromeHandler->RemoveSystemEventListener(NS_LITERAL_STRING("DOMTitleChanged"),
this, /* useCapture = */ false);
// Careful; the call above may have removed the last strong reference to this
// class, so don't dereference |this| here.
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "nsGenericHTMLElement.h"
#include "nsIDOMHTMLFrameElement.h"
#include "nsIDOMMozBrowserFrame.h"
#include "nsIDOMEventListener.h"
#include "nsIWebProgressListener.h"
/**
@ -22,9 +23,11 @@ public:
nsGenericHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
mozilla::dom::FromParser aFromParser)
: nsGenericHTMLElement(aNodeInfo)
, mNetworkCreated(aFromParser == mozilla::dom::FROM_PARSER_NETWORK)
, mBrowserFrameListenersRegistered(false)
{
mNetworkCreated = aFromParser == mozilla::dom::FROM_PARSER_NETWORK;
}
virtual ~nsGenericHTMLFrameElement();
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
@ -60,6 +63,28 @@ public:
nsGenericHTMLElement)
protected:
/**
* Listens to titlechanged events from the document inside the iframe and
* forwards them along to the iframe so it can fire a mozbrowsertitlechange
* event if appropriate.
*/
class TitleChangedListener : public nsIDOMEventListener
{
public:
TitleChangedListener(nsGenericHTMLFrameElement *aElement,
nsIDOMEventTarget *aChromeHandler);
/* Unregister this listener. */
void Unregister();
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
private:
nsWeakPtr mElement; /* nsGenericHTMLFrameElement */
nsWeakPtr mChromeHandler; /* nsIDOMEventTarget */
};
// This doesn't really ensure a frame loade in all cases, only when
// it makes sense.
nsresult EnsureFrameLoader();
@ -67,14 +92,19 @@ protected:
nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
nsresult GetContentWindow(nsIDOMWindow** aContentWindow);
void MaybeEnsureBrowserFrameListenersRegistered();
bool BrowserFrameSecurityCheck();
nsresult MaybeFireBrowserEvent(const nsAString &aEventName,
const nsAString &aEventType,
const nsAString &aValue = EmptyString());
nsRefPtr<nsFrameLoader> mFrameLoader;
nsRefPtr<TitleChangedListener> mTitleChangedListener;
// True when the element is created by the parser
// using NS_FROM_PARSER_NETWORK flag.
// If the element is modified, it may lose the flag.
bool mNetworkCreated;
bool mBrowserFrameListenersRegistered;
};

Просмотреть файл

@ -80,6 +80,7 @@ _TEST_FILES = \
test_browserFrame3.html \
test_browserFrame4.html \
test_browserFrame5.html \
test_browserFrame6.html \
$(NULL)
_CHROME_FILES = \

Просмотреть файл

@ -25,7 +25,10 @@ SimpleTest.waitForExplicitFinish();
function runTest() {
browserFrameHelpers.setEnabledPref(false);
var iframe = document.getElementById('iframe');
var iframe = document.createElement('iframe');
iframe.mozbrowser = true;
document.body.appendChild(iframe);
iframe.addEventListener('mozbrowserloadstart', function() {
ok(false, 'Should not send mozbrowserloadstart event.');
});
@ -42,7 +45,5 @@ addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
</script>
<iframe id='iframe' mozbrowser></iframe>
</body>
</html>

Просмотреть файл

@ -26,7 +26,10 @@ function runTest() {
browserFrameHelpers.setEnabledPref(true);
browserFrameHelpers.setWhitelistPref(' http://foobar.com');
var iframe = document.getElementById('iframe');
var iframe = document.createElement('iframe');
iframe.mozbrowser = true;
document.body.appendChild(iframe);
iframe.addEventListener('mozbrowserloadstart', function() {
ok(false, 'Should not send mozbrowserloadstart event.');
});
@ -43,7 +46,5 @@ addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
</script>
<iframe id='iframe' mozbrowser></iframe>
</body>
</html>

Просмотреть файл

@ -18,7 +18,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=710231
-->
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
@ -31,8 +30,24 @@ function runTest() {
browserFrameHelpers.setEnabledPref(true);
browserFrameHelpers.addToWhitelist();
var iframe = document.getElementById('iframe');
// Load example.org into the iframe, wait for that to load, then call
// runTest2. This would *almost* work if we just had a <iframe mozbrowser>
// in the HTML, except that we have to set the prefs before we create the
// iframe!
var iframe = document.createElement('iframe');
iframe.id = 'iframe';
document.body.appendChild(iframe);
iframe.src = 'data:text/html,1';
iframe.addEventListener('load', function() {
iframe.removeEventListener('load', arguments.callee);
SimpleTest.executeSoon(runTest2);
});
}
function runTest2() {
var iframe = document.getElementById('iframe');
iframe.mozbrowser = true;
iframe.addEventListener('mozbrowserloadstart', function() {
ok(!seenLoadStart, 'Just one loadstart event.');
seenLoadStart = true;
@ -82,7 +97,5 @@ addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
</script>
<iframe id='iframe' mozbrowser></iframe>
</body>
</html>

Просмотреть файл

@ -17,7 +17,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=720157
-->
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
@ -25,32 +24,62 @@ function runTest() {
browserFrameHelpers.setEnabledPref(true);
browserFrameHelpers.addToWhitelist();
var iframe = document.getElementById('iframe');
var iframe1 = document.createElement('iframe');
document.body.appendChild(iframe1);
iframe1.id = 'iframe1';
iframe1.addEventListener('load', function() {
iframe1.removeEventListener('load', arguments.callee);
SimpleTest.executeSoon(runTest2);
});
iframe1.src = 'http://example.org';
}
function runTest2() {
var iframe1 = document.getElementById('iframe1');
iframe1.mozbrowser = true;
var iframe2 = document.getElementById('iframe2');
var sawLoad = false;
var sawLocationChange = false;
iframe.addEventListener('mozbrowserlocationchange', function(e) {
iframe1.addEventListener('mozbrowserlocationchange', function(e) {
ok(!sawLocationChange, 'Just one locationchange event.');
ok(!sawLoad, 'locationchange before load.');
is(e.detail, 'data:text/html,1', "event's reported location");
sawLocationChange = true;
});
iframe.addEventListener('load', function() {
iframe1.addEventListener('load', function() {
ok(sawLocationChange, 'Load after locationchange.');
ok(!sawLoad, 'Just one load event.');
sawLoad = true;
SimpleTest.finish();
});
iframe.src = 'data:text/html,1';
function iframe2Load() {
if (!sawLoad || !sawLocationChange) {
// Spin if iframe1 hasn't loaded yet.
SimpleTest.executeSoon(iframe2Load);
return;
}
ok(true, 'Got iframe2 load.');
SimpleTest.finish();
}
iframe2.addEventListener('load', iframe2Load);
iframe1.src = 'data:text/html,1';
// Load something into iframe2 to check that it doesn't trigger a
// locationchange for our iframe1 listener.
iframe2.src = 'http://example.com';
}
addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
</script>
<iframe id='iframe' mozbrowser></iframe>
<iframe id='iframe2'></iframe>
</body>
</html>

Просмотреть файл

@ -0,0 +1,72 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=720157
-->
<head>
<title>Test for Bug 720157</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserFrameHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=720157">Mozilla Bug 720157</a>
<!--
Test that the onmozbrowsertitlechange event works.
-->
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
function runTest() {
browserFrameHelpers.setEnabledPref(true);
browserFrameHelpers.addToWhitelist();
var iframe1 = document.createElement('iframe');
iframe1.mozbrowser = true;
document.body.appendChild(iframe1);
// iframe2 is a red herring; we modify its title but don't listen for
// titlechanges; we want to make sure that its titlechange events aren't
// picked up by the listener on iframe1.
var iframe2 = document.createElement('iframe');
iframe2.mozbrowser = true;
document.body.appendChild(iframe2);
var numTitleChanges = 0;
iframe1.addEventListener('mozbrowsertitlechange', function(e) {
numTitleChanges++;
if (numTitleChanges == 1) {
is(e.detail, 'Title');
iframe1.contentDocument.title = 'New title';
iframe2.contentDocument.title = 'BAD TITLE 2';
}
else if (numTitleChanges == 2) {
is(e.detail, 'New title');
iframe1.src = 'data:text/html,<html><head><title>Title 3</title></head><body></body></html>';
}
else if (numTitleChanges == 3) {
is(e.detail, 'Title 3');
SimpleTest.finish();
}
else {
ok(false, 'Too many titlechange events.');
}
});
iframe1.src = 'data:text/html,<html><head><title>Title</title></head><body></body></html>';
iframe2.src = 'data:text/html,<html><head><title>BAD TITLE</title></head><body></body></html>';
}
addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
</script>
</body>
</html>