Bug 708176 - Part 2: Add {i,}frame.queryInnerState(), which allows privileged pages to peer into an iframe's state. r=smaug

--HG--
extra : rebase_source : dfd89eb7d451bac9ae4e4eb05d090c767f42ab8e
This commit is contained in:
Justin Lebar 2012-01-10 12:57:40 -05:00
parent a24aacc4db
commit e1048acb82
8 changed files with 332 additions and 3 deletions

View File

@ -168,6 +168,7 @@ GK_ATOM(broadcast, "broadcast")
GK_ATOM(broadcaster, "broadcaster")
GK_ATOM(broadcasterset, "broadcasterset")
GK_ATOM(browser, "browser")
GK_ATOM(mozbrowser, "mozbrowser")
GK_ATOM(bulletinboard, "bulletinboard")
GK_ATOM(button, "button")
GK_ATOM(callTemplate, "call-template")

View File

@ -2559,6 +2559,7 @@ nsGenericHTMLElement::GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu)
//----------------------------------------------------------------------
NS_IMPL_INT_ATTR(nsGenericHTMLFrameElement, TabIndex, tabindex)
NS_IMPL_BOOL_ATTR(nsGenericHTMLFrameElement, MozBrowser, mozbrowser)
nsGenericHTMLFormElement::nsGenericHTMLFormElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
@ -3227,8 +3228,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_TABLE_HEAD(nsGenericHTMLFrameElement)
NS_INTERFACE_TABLE_INHERITED1(nsGenericHTMLFrameElement,
nsIFrameLoaderOwner)
NS_INTERFACE_TABLE_INHERITED2(nsGenericHTMLFrameElement,
nsIFrameLoaderOwner,
nsIDOMMozBrowserFrameElement)
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsGenericHTMLFrameElement)
NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
@ -3435,6 +3437,108 @@ nsGenericHTMLFrameElement::SizeOf() const
return size;
}
namespace {
// GetContentStateCallbackRunnable is used by MozGetContentState to fire its callback
// asynchronously.
class GetContentStateCallbackRunnable : public nsRunnable
{
public:
GetContentStateCallbackRunnable(nsIDOMMozGetContentStateCallback *aCallback,
nsIDOMEventTarget *aEventTarget,
const nsAString &aResult)
: mCallback(aCallback)
, mEventTarget(aEventTarget)
, mResult(aResult)
{
}
NS_IMETHOD Run()
{
FireCallback();
// Break cycles.
mCallback = NULL;
mEventTarget = NULL;
return NS_OK;
}
private:
void FireCallback()
{
nsCxPusher pusher;
if (!pusher.Push(mEventTarget)) {
return;
}
mCallback->Callback(mResult);
}
nsCOMPtr<nsIDOMMozGetContentStateCallback> mCallback;
nsCOMPtr<nsIDOMEventTarget> mEventTarget;
const nsString mResult;
};
} // anonymous namespace
nsresult
nsGenericHTMLFrameElement::BrowserFrameSecurityCheck()
{
if (!Preferences::GetBool("dom.mozBrowserFramesEnabled")) {
return NS_ERROR_FAILURE;
}
bool browser;
GetMozBrowser(&browser);
if (!browser) {
return NS_ERROR_FAILURE;
}
nsIPrincipal *principal = NodePrincipal();
nsCOMPtr<nsIURI> principalURI;
principal->GetURI(getter_AddRefs(principalURI));
if (!nsContentUtils::URIIsChromeOrInPref(principalURI,
"dom.mozBrowserFramesWhitelist")) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::MozGetContentState(const nsAString &aProperty,
nsIDOMMozGetContentStateCallback *aCallback)
{
nsresult rv = BrowserFrameSecurityCheck();
NS_ENSURE_SUCCESS(rv, rv);
if (!aProperty.EqualsLiteral("location")) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMWindow> contentWindow;
GetContentWindow(getter_AddRefs(contentWindow));
NS_ENSURE_TRUE(contentWindow, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMLocation> location;
rv = contentWindow->GetLocation(getter_AddRefs(location));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString href;
rv = location->ToString(href);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMEventTarget> eventTarget =
do_QueryInterface(nsContentUtils::GetWindowFromCaller());
NS_ENSURE_TRUE(eventTarget, NS_ERROR_FAILURE);
// Asynchronously fire the callback.
nsRefPtr<GetContentStateCallbackRunnable> runnable =
new GetContentStateCallbackRunnable(aCallback, eventTarget, href);
NS_DispatchToMainThread(runnable);
return NS_OK;
}
//----------------------------------------------------------------------
nsresult

View File

@ -47,6 +47,7 @@
#include "nsGkAtoms.h"
#include "nsContentCreatorFunctions.h"
#include "nsDOMMemoryReporter.h"
#include "nsIDOMMozBrowserFrameElement.h"
class nsIDOMAttr;
class nsIDOMEventListener;
@ -1019,7 +1020,8 @@ PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32);
*/
class nsGenericHTMLFrameElement : public nsGenericHTMLElement,
public nsIFrameLoaderOwner
public nsIFrameLoaderOwner,
public nsIDOMMozBrowserFrameElement
{
public:
nsGenericHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
@ -1064,6 +1066,9 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsGenericHTMLFrameElement,
nsGenericHTMLElement)
// nsIDOMMozBrowserFrameElement
NS_DECL_NSIDOMMOZBROWSERFRAMEELEMENT
protected:
// This doesn't really ensure a frame loade in all cases, only when
// it makes sense.
@ -1072,6 +1077,8 @@ protected:
nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
nsresult GetContentWindow(nsIDOMWindow** aContentWindow);
nsresult BrowserFrameSecurityCheck();
nsRefPtr<nsFrameLoader> mFrameLoader;
// True when the element is created by the parser
// using NS_FROM_PARSER_NETWORK flag.

View File

@ -140,6 +140,7 @@
#include "nsIDOMDOMStringList.h"
#include "nsIDOMDOMTokenList.h"
#include "nsIDOMDOMSettableTokenList.h"
#include "nsIDOMMozBrowserFrameElement.h"
#include "nsDOMStringMap.h"
@ -2671,6 +2672,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_BEGIN(HTMLFrameElement, nsIDOMHTMLFrameElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLFrameElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozBrowserFrameElement)
DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
@ -2702,6 +2704,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_BEGIN(HTMLIFrameElement, nsIDOMHTMLIFrameElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLIFrameElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMGetSVGDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozBrowserFrameElement)
DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
DOM_CLASSINFO_MAP_END

View File

@ -116,6 +116,7 @@ SDK_XPIDLSRCS = \
nsIDOMHTMLAudioElement.idl \
nsIDOMValidityState.idl \
nsIDOMDOMStringMap.idl \
nsIDOMMozBrowserFrameElement.idl \
$(NULL)
XPIDLSRCS = \

View File

@ -0,0 +1,68 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Lebar <justin.lebar@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
[scriptable, function, uuid(37687881-1801-489f-ad03-7af651a93448)]
interface nsIDOMMozGetContentStateCallback : nsISupports
{
void callback(in DOMString value);
};
[scriptable, uuid(2ff0f421-64e4-4186-b0dd-619629f46048)]
interface nsIDOMMozBrowserFrameElement : nsISupports
{
/**
* If true, a privileged page can call mozGetContentState on this element.
* If false, mozGetContentState will fail.
*/
attribute boolean mozBrowser;
/**
* Get a piece of state from this element's content window, returning its
* value via |callback|.
*
* At the moment, the only valid property is "location", which returns the
* content window's location. Passing any other property causes an error.
*
* If the iframe's mozBrowser is false, or if the calling window is not
* privileged, this function fails.
*/
void mozGetContentState(in DOMString property,
in nsIDOMMozGetContentStateCallback callback);
};

View File

@ -74,6 +74,7 @@ _TEST_FILES = \
test_focusrings.xul \
file_moving_xhr.html \
test_vibrator.html \
test_getContentState.html \
$(NULL)
_CHROME_FILES = \

View File

@ -0,0 +1,144 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=708963
-->
<head>
<title>Test for Bug 708963</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=708963">Mozilla Bug 708963</a>
<script type="application/javascript;version=1.7">
"use strict";
function getEnabledPref() {
try {
return SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
}
catch(e) {
return undefined;
}
}
function getWhitelistPref() {
try {
return SpecialPowers.getCharPref('dom.mozBrowserFramesWhitelist');
}
catch(e) {
return undefined;
}
}
function setPrefs(enabled, whitelist) {
if (enabled !== undefined) {
SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', enabled);
}
else {
SpecialPowers.clearUserPref('dom.mozBrowserFramesEnabled');
}
if (whitelist !== undefined) {
SpecialPowers.setCharPref('dom.mozBrowserFramesWhitelist', whitelist);
}
else {
SpecialPowers.clearUserPref('dom.mozBrowserFramesWhitelist');
}
}
function getPrePath() {
return 'http://' + window.location.host;
}
function checkCannotQueryIframe(id) {
let iframe = document.getElementById(id);
try {
iframe.mozGetContentState('location', function() {
ok(false, 'Got callback, but should have failed.');
});
ok(false, 'Should have thrown exception.');
}
catch(e) {
ok(true, 'Got expected exception.');
}
}
function checkCannotQuery() {
checkCannotQueryIframe('queryable');
checkCannotQueryIframe('not-queryable');
}
let numCallbacksExpected = 0;
let numCallbacksSeen = 0;
function checkCanQuery() {
checkCannotQueryIframe('not-queryable');
let iframe = document.getElementById('queryable');
iframe.mozGetContentState('location', function(href) {
ok(true, 'Got callback, as expected.');
is(href, 'http://example.com/', "Callback value.");
numCallbacksSeen++;
});
numCallbacksExpected++;
}
const oldEnabled = getEnabledPref();
const oldWhitelist = getWhitelistPref();
function runTest() {
setPrefs(false, getPrePath());
checkCannotQuery();
setPrefs(true, '');
checkCannotQuery();
setPrefs(true, getPrePath());
checkCanQuery();
setPrefs(true, 'http://example.com , ' +
getPrePath().toUpperCase() + ', ');
checkCanQuery();
// Spin if we haven't seen all the expected callbacks.
waitForAllExpectedCallbacks();
}
function waitForAllExpectedCallbacks() {
if (numCallbacksSeen < numCallbacksExpected) {
SimpleTest.executeSoon(waitForAllExpectedCallbacks);
return;
}
// Spin the event loop a few times to let any remaining pending callbacks
// fire. (If we get any callbacks here, the test should fail.)
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(function() {
setPrefs(oldEnabled, oldWhitelist);
SimpleTest.finish();
});
});
});
}
SimpleTest.waitForExplicitFinish();
var numLoaded = 0;
function iframeLoaded() {
numLoaded++;
if (numLoaded == 2) {
runTest();
}
}
</script>
<iframe onload='iframeLoaded()' id='not-queryable' src='http://example.net'></iframe>
<iframe onload='iframeLoaded()' mozbrowser id='queryable' src='http://example.com'></iframe>
</body>
</html>