Merge inbound to central, a=merge

MozReview-Commit-ID: 4hSCKRdSJmM
This commit is contained in:
Wes Kocher 2016-04-13 14:18:44 -07:00
commit 6248f1e33c
657 changed files with 3581 additions and 97346 deletions

View File

@ -793,24 +793,13 @@ getParentCB(AtkObject *aAtkObj)
if (aAtkObj->accessible_parent)
return aAtkObj->accessible_parent;
AtkObject* atkParent = nullptr;
if (AccessibleWrap* wrapper = GetAccessibleWrap(aAtkObj)) {
Accessible* parent = wrapper->Parent();
atkParent = parent ? AccessibleWrap::GetAtkObject(parent) : nullptr;
} else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
ProxyAccessible* parent = proxy->Parent();
if (parent) {
atkParent = GetWrapperFor(parent);
} else {
// Otherwise this should be the proxy for the tab's top level document.
Accessible* outerDocParent = proxy->OuterDocOfRemoteBrowser();
NS_ASSERTION(outerDocParent, "this document should have an outerDoc as a parent");
if (outerDocParent) {
atkParent = AccessibleWrap::GetAtkObject(outerDocParent);
}
}
AccessibleOrProxy acc = GetInternalObj(aAtkObj);
if (acc.IsNull()) {
return nullptr;
}
AccessibleOrProxy parent = acc.Parent();
AtkObject* atkParent = !parent.IsNull() ? GetWrapperFor(parent) : nullptr;
if (atkParent)
atk_object_set_parent(aAtkObj, atkParent);
@ -1101,6 +1090,16 @@ GetWrapperFor(ProxyAccessible* aProxy)
return reinterpret_cast<AtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
}
AtkObject*
GetWrapperFor(AccessibleOrProxy aObj)
{
if (aObj.IsProxy()) {
return GetWrapperFor(aObj.AsProxy());
}
return AccessibleWrap::GetAtkObject(aObj.AsAccessible());
}
static uint16_t
GetInterfacesForProxy(ProxyAccessible* aProxy, uint32_t aInterfaces)
{

View File

@ -69,6 +69,7 @@ mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
mozilla::a11y::AccessibleOrProxy GetInternalObj(AtkObject* aObj);
AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
AtkObject* GetWrapperFor(mozilla::a11y::AccessibleOrProxy aObj);
extern int atkMajorVersion, atkMinorVersion;

View File

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccessibleOrProxy.h"
AccessibleOrProxy
AccessibleOrProxy::Parent() const
{
if (IsAccessible()) {
return AsAccessible()->Parent();
}
ProxyAccessible* proxy = AsProxy();
if (!proxy) {
return nullptr;
}
if (ProxyAccessible* parent = proxy->Parent()) {
return parent;
}
// Otherwise this should be the proxy for the tab's top level document.
return proxy->OuterDocOfRemoteBrowser();
}

View File

@ -106,6 +106,8 @@ public:
return AsAccessible()->Role();
}
AccessibleOrProxy Parent() const;
// XXX these are implementation details that ideally would not be exposed.
uintptr_t Bits() const { return mBits; }
void SetBits(uintptr_t aBits) { mBits = aBits; }

View File

@ -237,7 +237,7 @@ EventTree*
EventTree::FindOrInsert(Accessible* aContainer)
{
if (!mFirst) {
return mFirst = new EventTree(aContainer);
return mFirst = new EventTree(aContainer, true);
}
EventTree* prevNode = nullptr;
@ -253,79 +253,24 @@ EventTree::FindOrInsert(Accessible* aContainer)
}
// Check if the given container is contained by a current node
Accessible* tailRoot = aContainer->Document();
Accessible* tailParent = aContainer;
EventTree* matchNode = nullptr;
Accessible* matchParent = nullptr;
while (true) {
Accessible* top = mContainer ? mContainer : aContainer->Document();
Accessible* parent = aContainer;
while (parent) {
// Reached a top, no match for a current event.
if (tailParent == tailRoot) {
// If we have a match in parents then continue to look in siblings.
if (matchNode && node->mNext) {
node = node->mNext;
if (node->mContainer == aContainer) {
return node; // case of same target
}
tailParent = aContainer;
continue;
}
if (parent == top) {
break;
}
// We got a match.
if (tailParent->Parent() == node->mContainer) {
matchNode = node;
matchParent = tailParent;
// Search the subtree for a better match.
if (node->mFirst) {
tailRoot = node->mContainer;
node = node->mFirst;
if (node->mContainer == aContainer) {
return node; // case of same target
}
tailParent = aContainer;
continue;
}
break;
if (parent->Parent() == node->mContainer) {
return node->FindOrInsert(aContainer);
}
tailParent = tailParent->Parent();
MOZ_ASSERT(tailParent, "Wrong tree");
if (!tailParent) {
break;
}
parent = parent->Parent();
MOZ_ASSERT(parent, "Wrong tree");
}
// The given node is contained by a current node
// if hide of a current node contains the given node
// then assert
// if show of a current node contains the given node
// then ignore the given node
// otherwise ignore the given node, but not its show and hide events
if (matchNode) {
uint32_t eventType = 0;
uint32_t count = matchNode->mDependentEvents.Length();
for (uint32_t idx = count - 1; idx < count; idx--) {
if (matchNode->mDependentEvents[idx]->mAccessible == matchParent) {
eventType = matchNode->mDependentEvents[idx]->mEventType;
}
}
MOZ_ASSERT(eventType != nsIAccessibleEvent::EVENT_HIDE,
"Accessible tree was modified after it was removed");
// If contained by show event target then no events are required.
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
return nullptr;
}
node->mFirst = new EventTree(aContainer);
node->mFirst->mFireReorder = false;
return node->mFirst;
}
// If the given node contains a current node
// If the given container contains a current node
// then
// if show or hide of the given node contains a grand parent of the current node
// then ignore the current node and its show and hide events
@ -341,7 +286,7 @@ EventTree::FindOrInsert(Accessible* aContainer)
// its parent.
node->mFireReorder = false;
nsAutoPtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
nsAutoPtr<EventTree> newNode(new EventTree(aContainer));
nsAutoPtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
newNode->mFirst = Move(nodeOwnerRef);
nodeOwnerRef = Move(newNode);
nodeOwnerRef->mNext = Move(node->mNext);
@ -383,7 +328,14 @@ EventTree::FindOrInsert(Accessible* aContainer)
} while ((node = node->mNext));
MOZ_ASSERT(prevNode, "Nowhere to insert");
return prevNode->mNext = new EventTree(aContainer);
MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
// If 'this' node contains the given container accessible, then
// do not emit a reorder event for the container
// if a dependent show event target contains the given container then do not
// emit show / hide events (to be done)
return prevNode->mNext = new EventTree(aContainer, mDependentEvents.IsEmpty());
}
void

View File

@ -57,8 +57,9 @@ class EventTree final {
public:
EventTree() :
mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(false) { }
explicit EventTree(Accessible* aContainer) :
mFirst(nullptr), mNext(nullptr), mContainer(aContainer), mFireReorder(true) { }
explicit EventTree(Accessible* aContainer, bool aFireReorder) :
mFirst(nullptr), mNext(nullptr), mContainer(aContainer),
mFireReorder(aFireReorder) { }
~EventTree() { Clear(); }
void Shown(Accessible* aChild)

View File

@ -26,6 +26,7 @@ if CONFIG['MOZ_DEBUG']:
]
UNIFIED_SOURCES += [
'AccessibleOrProxy.cpp',
'AccEvent.cpp',
'AccGroupInfo.cpp',
'AccIterator.cpp',

View File

@ -421,6 +421,44 @@
}
}
/**
* Remove a child, remove a parent sibling, remove the parent
*/
function test5()
{
this.o = getAccessible("t5_o");
this.ofc = this.o.firstChild;
this.b = getAccessible("t5_b");
this.lb = getAccessible("t5_lb");
this.eventSeq = [
new invokerChecker(EVENT_HIDE, this.o),
new invokerChecker(EVENT_HIDE, this.b),
new invokerChecker(EVENT_REORDER, "t5"),
new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
new unexpectedInvokerChecker(EVENT_REORDER, this.o),
new unexpectedInvokerChecker(EVENT_REORDER, this.lb)
];
this.invoke = function test5_invoke()
{
getNode("t5_o").textContent = "";
getNode("t5").removeChild(getNode("t5_b"));
getNode("t5_lb").removeChild(getNode("t5_o"));
}
this.finalCheck = function test5_finalCheck()
{
testIsDefunct(this.ofc);
testIsDefunct(this.o);
testIsDefunct(this.b);
}
this.getID = function test5_getID() {
return "remove a child, remove a parent sibling, remove the parent";
}
}
////////////////////////////////////////////////////////////////////////////
// Do tests.
@ -451,6 +489,7 @@
gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
gQueue.push(new test3());
gQueue.push(new test4());
gQueue.push(new test5());
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -534,5 +573,12 @@
<div role="option" id="t4_o2">opt2</div>
</div>
</div>
<div id="t5">
<div role="button" id="t5_b">btn</div>
<div role="listbox" id="t5_lb">
<div role="option" id="t5_o">opt</div>
</div>
</div>
</body>
</html>

View File

@ -25,10 +25,11 @@ xpcAccessible::GetParent(nsIAccessible** aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
*aParent = nullptr;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
NS_IF_ADDREF(*aParent = ToXPC(Intl()->Parent()));
AccessibleOrProxy parent = IntlGeneric().Parent();
NS_IF_ADDREF(*aParent = ToXPC(parent));
return NS_OK;
}
@ -40,12 +41,17 @@ xpcAccessible::GetNextSibling(nsIAccessible** aNextSibling)
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
if (!Intl())
return NS_ERROR_FAILURE;
if (IntlGeneric().IsAccessible()) {
nsresult rv = NS_OK;
NS_IF_ADDREF(*aNextSibling = ToXPC(Intl()->GetSiblingAtOffset(1, &rv)));
return rv;
}
nsresult rv = NS_OK;
NS_IF_ADDREF(*aNextSibling = ToXPC(Intl()->GetSiblingAtOffset(1, &rv)));
return rv;
ProxyAccessible* proxy = IntlGeneric().AsProxy();
NS_ENSURE_STATE(proxy);
NS_IF_ADDREF(*aNextSibling = ToXPC(proxy->NextSibling()));
return *aNextSibling ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
@ -56,12 +62,17 @@ xpcAccessible::GetPreviousSibling(nsIAccessible** aPreviousSibling)
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
if (!Intl())
return NS_ERROR_FAILURE;
if (IntlGeneric().IsAccessible()) {
nsresult rv = NS_OK;
NS_IF_ADDREF(*aPreviousSibling = ToXPC(Intl()->GetSiblingAtOffset(-1, &rv)));
return rv;
}
nsresult rv = NS_OK;
NS_IF_ADDREF(*aPreviousSibling = ToXPC(Intl()->GetSiblingAtOffset(-1, &rv)));
return rv;
ProxyAccessible* proxy = IntlGeneric().AsProxy();
NS_ENSURE_STATE(proxy);
NS_IF_ADDREF(*aPreviousSibling = ToXPC(proxy->PrevSibling()));
return *aPreviousSibling ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
@ -156,10 +167,12 @@ xpcAccessible::GetIndexInParent(int32_t* aIndexInParent)
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
if (!Intl())
return NS_ERROR_FAILURE;
if (IntlGeneric().IsAccessible()) {
*aIndexInParent = Intl()->IndexInParent();
} else if (IntlGeneric().IsProxy()) {
*aIndexInParent = IntlGeneric().AsProxy()->IndexInParent();
}
*aIndexInParent = Intl()->IndexInParent();
return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE;
}

View File

@ -1059,9 +1059,6 @@ pref("layout.accessiblecaret.bar.enabled", true);
pref("layout.accessiblecaret.use_long_tap_injector", false);
#endif
// The active caret is disallow to be dragged across the other (inactive) caret.
pref("layout.accessiblecaret.allow_dragging_across_other_caret", false);
// Enable sync and mozId with Firefox Accounts.
pref("services.sync.fxaccounts.enabled", true);
pref("identity.fxaccounts.enabled", true);

View File

@ -3720,9 +3720,12 @@ function FillHistoryMenu(aParent) {
// if there is only one entry now, close the popup.
aParent.hidePopup();
return;
} else if (!aParent.parentNode.open) {
} else if (aParent.id != "backForwardMenu" && !aParent.parentNode.open) {
// if the popup wasn't open before, but now needs to be, reopen the menu.
// It should trigger FillHistoryMenu again.
// It should trigger FillHistoryMenu again. This might happen with the
// delay from click-and-hold menus but skip this for the context menu
// (backForwardMenu) rather than figuring out how the menu should be
// positioned and opened as it is an extreme edgecase.
aParent.parentNode.open = true;
return;
}

View File

@ -848,6 +848,28 @@ var RefreshBlocker = {
RefreshBlocker.init();
var UserContextIdNotifier = {
init() {
addEventListener("DOMContentLoaded", this);
},
uninit() {
removeEventListener("DOMContentLoaded", this);
},
handleEvent(aEvent) {
// When the first content is loaded, we want to inform the tabbrowser about
// the userContextId in use in order to update the UI correctly.
// Just because we cannot change the userContextId from an active docShell,
// we don't need to check DOMContentLoaded again.
this.uninit();
let userContextId = content.document.nodePrincipal.originAttributes.userContextId;
sendAsyncMessage("Browser:FirstContentLoaded", { userContextId });
}
};
UserContextIdNotifier.init();
ExtensionContent.init(this);
addEventListener("unload", () => {
ExtensionContent.uninit(this);

View File

@ -4285,6 +4285,14 @@
browser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned })
break;
}
case "Browser:FirstContentLoaded": {
let tab = this.getTabForBrowser(browser);
if (tab && data.userContextId) {
tab.setUserContextId(data.userContextId);
}
break;
}
case "Findbar:Keypress": {
let tab = this.getTabForBrowser(browser);
// If the find bar for this tab is not yet alive, only initialize
@ -4431,6 +4439,7 @@
messageManager.addMessageListener("DOMWebNotificationClicked", this);
messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
messageManager.addMessageListener("RefreshBlocker:Blocked", this);
messageManager.addMessageListener("Browser:FirstContentLoaded", this);
// To correctly handle keypresses for potential FindAsYouType, while
// the tab's find bar is not yet initialized.

View File

@ -26,13 +26,26 @@ const PAGECONTENT =
" </optgroup></select><input />Text" +
"</body></html>";
function openSelectPopup(selectPopup, withMouse)
const PAGECONTENT_SMALL =
"<html>" +
"<body><select id='one'>" +
" <option value='One'>One</option>" +
" <option value='Two'>Two</option>" +
"</select><select id='two'>" +
" <option value='Three'>Three</option>" +
" <option value='Four'>Four</option>" +
"</select><select id='three'>" +
" <option value='Five'>Five</option>" +
" <option value='Six'>Six</option>" +
"</select></body></html>";
function openSelectPopup(selectPopup, withMouse, selector = "select")
{
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
if (withMouse) {
return Promise.all([popupShownPromise,
BrowserTestUtils.synthesizeMouseAtCenter("select", { }, gBrowser.selectedBrowser)]);
BrowserTestUtils.synthesizeMouseAtCenter(selector, { }, gBrowser.selectedBrowser)]);
}
setTimeout(() => EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }), 1500);
@ -41,7 +54,7 @@ function openSelectPopup(selectPopup, withMouse)
function hideSelectPopup(selectPopup, withEscape)
{
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
if (withEscape) {
EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" });
@ -50,7 +63,7 @@ function hideSelectPopup(selectPopup, withEscape)
EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" });
}
return popupShownPromise;
return popupHiddenPromise;
}
function getChangeEvents()
@ -62,11 +75,8 @@ function getChangeEvents()
function doSelectTests(contentType, dtd)
{
let tab = gBrowser.selectedTab = gBrowser.addTab();
let browser = gBrowser.getBrowserForTab(tab);
yield promiseTabLoadEvent(tab, "data:" + contentType + "," + escape(dtd + "\n" + PAGECONTENT));
yield SimpleTest.promiseFocus(browser.contentWindow);
const pageUrl = "data:" + contentType + "," + escape(dtd + "\n" + PAGECONTENT);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
let menulist = document.getElementById("ContentSelectDropdown");
let selectPopup = menulist.menupopup;
@ -138,7 +148,7 @@ function doSelectTests(contentType, dtd)
is(selectPopup.lastChild.previousSibling.label, "Seven", "Spaces collapsed");
is(selectPopup.lastChild.label, "\xA0\xA0Eight\xA0\xA0", "Non-breaking spaces not collapsed");
gBrowser.removeCurrentTab();
yield BrowserTestUtils.removeTab(tab);
}
add_task(function*() {
@ -149,3 +159,46 @@ add_task(function*() {
yield doSelectTests("application/xhtml+xml", XHTML_DTD);
});
// This test opens a select popup and removes the content node of a popup while
// The popup should close if its node is removed.
add_task(function*() {
const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
let menulist = document.getElementById("ContentSelectDropdown");
let selectPopup = menulist.menupopup;
// First, try it when a different <select> element than the one that is open is removed
yield openSelectPopup(selectPopup, true, "#one");
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.document.body.removeChild(content.document.getElementById("two"));
});
// Wait a bit just to make sure the popup won't close.
yield new Promise(resolve => setTimeout(resolve, 1000));
is(selectPopup.state, "open", "Different popup did not affect open popup");
yield hideSelectPopup(selectPopup);
// Next, try it when the same <select> element than the one that is open is removed
yield openSelectPopup(selectPopup, true, "#three");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.document.body.removeChild(content.document.getElementById("three"));
});
yield popupHiddenPromise;
ok(true, "Popup hidden when select is removed");
// Finally, try it when the tab is closed while the select popup is open.
yield openSelectPopup(selectPopup, true, "#one");
popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
yield BrowserTestUtils.removeTab(tab);
yield popupHiddenPromise;
ok(true, "Popup hidden when tab is closed");
});

View File

@ -6,3 +6,4 @@ support-files =
[browser_usercontext.js]
[browser_windowName.js]
[browser_windowOpen.js]

View File

@ -0,0 +1,41 @@
"use strict";
// Here we want to test that a new opened window shows the same UI of the
// parent one if this has been loaded from a particular container.
const BASE_URI = "http://mochi.test:8888/browser/browser/components/"
+ "contextualidentity/test/browser/empty_file.html";
add_task(function* setup() {
yield new Promise((resolve) => {
SpecialPowers.pushPrefEnv({"set": [
["privacy.userContext.enabled", true],
["browser.link.open_newwindow", 2],
]}, resolve);
});
});
add_task(function* test() {
info("Creating a tab with UCI = 1...");
let tab = gBrowser.addTab(BASE_URI, {userContextId: 1});
is(tab.getAttribute('usercontextid'), 1, "New tab has UCI equal 1");
let browser = gBrowser.getBrowserForTab(tab);
yield BrowserTestUtils.browserLoaded(browser);
info("Opening a new window from this tab...");
ContentTask.spawn(browser, BASE_URI, function(url) {
content.window.newWindow = content.window.open(url, "_blank");
});
let newWin = yield BrowserTestUtils.waitForNewWindow();
let newTab = newWin.gBrowser.selectedTab;
yield BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
is(newTab.getAttribute('usercontextid'), 1, "New tab has UCI equal 1");
info("Closing the new window and tab...");
yield BrowserTestUtils.closeWindow(newWin);
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -21,8 +21,6 @@ COMPILER msvc _MSC_FULL_VER
COMPILER clang __clang_major__.__clang_minor__.__clang_patchlevel__
#elif defined(__GNUC__)
COMPILER gcc __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
#elif defined(__INTEL_COMPILER)
COMPILER icc __INTEL_COMPILER
#endif
EOF
read dummy compiler CC_VERSION <<EOF
@ -68,13 +66,6 @@ if test "$compiler" = "msvc"; then
CXX_VERSION=`echo ${CXX_VERSION} | cut -c 1-4`
fi
INTEL_CC=
INTEL_CXX=
if test "$compiler" = "icc"; then
INTEL_CC=1
INTEL_CXX=1
fi
CLANG_CC=
CLANG_CXX=
CLANG_CL=

View File

@ -129,13 +129,16 @@ def extra_old_configure_args(help):
@template
@imports(_from='mozbuild.configure', _import='DependsFunction')
def add_old_configure_assignment(var, value_func):
assert isinstance(value_func, DependsFunction)
def add_old_configure_assignment(var, value):
if not isinstance(var, DependsFunction):
var = depends('--help')(lambda x: var)
if not isinstance(value, DependsFunction):
value = depends('--help')(lambda x: value)
@depends(old_configure_assignments, value_func)
@depends(old_configure_assignments, var, value)
@imports(_from='mozbuild.shellutil', _import='quote')
def add_assignment(assignments, value):
if value is None:
def add_assignment(assignments, var, value):
if var is None or value is None:
return
if value is True:
assignments.append('%s=1' % var)
@ -143,7 +146,7 @@ def add_old_configure_assignment(var, value_func):
assignments.append('%s=' % var)
else:
if isinstance(value, (list, tuple)):
value = ' '.join(quote(v) for v in value)
value = quote(*value)
assignments.append('%s=%s' % (var, quote(value)))
@template

View File

@ -396,7 +396,7 @@ def old_configure(prepare_configure, extra_old_configure_args, all_options,
cmd += extra_old_configure_args
# For debugging purpose, in case it's not what we'd expect.
log.debug('Running %s', ' '.join(quote(a) for a in cmd))
log.debug('Running %s', quote(*cmd))
# Our logging goes to config.log, the same file old.configure uses.
# We can't share the handle on the file, so close it. We assume nothing

View File

@ -520,7 +520,7 @@ function testGetterSetterObject() {
is(propNonEnums[0].querySelector(".name").getAttribute("value"), "get",
"Should have the right property name for 'get'.");
is(propNonEnums[0].querySelector(".value").getAttribute("value"),
"test/myVar.prop()",
"get prop()",
"Should have the right property value for 'get'.");
ok(propNonEnums[0].querySelector(".value").className.includes("token-other"),
"Should have the right token class for 'get'.");
@ -528,7 +528,7 @@ function testGetterSetterObject() {
is(propNonEnums[1].querySelector(".name").getAttribute("value"), "set",
"Should have the right property name for 'set'.");
is(propNonEnums[1].querySelector(".value").getAttribute("value"),
"test/myVar.prop(val)",
"set prop(val)",
"Should have the right property value for 'set'.");
ok(propNonEnums[1].querySelector(".value").className.includes("token-other"),
"Should have the right token class for 'set'.");

View File

@ -41,6 +41,8 @@ support-files =
[browser_toolbox_highlight.js]
[browser_toolbox_hosts.js]
[browser_toolbox_hosts_size.js]
[browser_toolbox_keyboard_navigation.js]
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_toolbox_minimize.js]
skip-if = true # Bug 1177463 - Temporarily hide the minimize button
[browser_toolbox_options.js]

View File

@ -0,0 +1,88 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests keyboard navigation of devtools tabbar.
const TEST_URL =
"data:text/html;charset=utf8,test page for toolbar keyboard navigation";
function containsFocus(aDoc, aElm) {
let elm = aDoc.activeElement;
while (elm) {
if (elm === aElm) { return true; }
elm = elm.parentNode;
}
return false;
}
function testFocus(aDoc, aToolbar, aElm) {
let id = aElm.id;
is(aToolbar.getAttribute("aria-activedescendant"), id,
`Active descendant is set to a new control: ${id}`);
is(aDoc.activeElement.id, id, "New control is focused");
}
add_task(function*() {
info("Create a test tab and open the toolbox");
let toolbox = yield openNewTabAndToolbox(TEST_URL, "webconsole");
let doc = toolbox.doc;
let toolbar = doc.querySelector(".devtools-tabbar");
let toolbarControls = [...toolbar.querySelectorAll(
".devtools-tab, toolbarbutton")].filter(elm =>
!elm.hidden && doc.defaultView.getComputedStyle(elm).getPropertyValue(
"display") !== "none");
// Put the keyboard focus onto the first toolbar control.
toolbarControls[0].focus();
ok(containsFocus(doc, toolbar), "Focus is within the toolbar");
// Move the focus away from toolbar to a next focusable element.
EventUtils.synthesizeKey("VK_TAB", {});
ok(!containsFocus(doc, toolbar), "Focus is outside of the toolbar");
// Move the focus back to the toolbar.
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
ok(containsFocus(doc, toolbar), "Focus is within the toolbar again");
// Move through the toolbar forward using the right arrow key.
for (let i = 0; i < toolbarControls.length; ++i) {
testFocus(doc, toolbar, toolbarControls[i]);
if (i < toolbarControls.length - 1) {
EventUtils.synthesizeKey("VK_RIGHT", {});
}
}
// Move the focus away from toolbar to a next focusable element.
EventUtils.synthesizeKey("VK_TAB", {});
ok(!containsFocus(doc, toolbar), "Focus is outside of the toolbar");
// Move the focus back to the toolbar.
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
ok(containsFocus(doc, toolbar), "Focus is within the toolbar again");
// Move through the toolbar backward using the left arrow key.
for (let i = toolbarControls.length - 1; i >= 0; --i) {
testFocus(doc, toolbar, toolbarControls[i]);
if (i > 0) { EventUtils.synthesizeKey("VK_LEFT", {}); }
}
// Move focus to the 3rd (non-first) toolbar control.
let expectedFocusedControl = toolbarControls[2];
EventUtils.synthesizeKey("VK_RIGHT", {});
EventUtils.synthesizeKey("VK_RIGHT", {});
testFocus(doc, toolbar, expectedFocusedControl);
// Move the focus away from toolbar to a next focusable element.
EventUtils.synthesizeKey("VK_TAB", {});
ok(!containsFocus(doc, toolbar), "Focus is outside of the toolbar");
// Move the focus back to the toolbar, ensure we land on the last active
// descendant control.
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
testFocus(doc, toolbar, expectedFocusedControl);
});

View File

@ -413,6 +413,7 @@ Toolbox.prototype = {
this._addZoomKeys();
this._loadInitialZoom();
}
this._setToolbarKeyboardNavigation();
this.webconsolePanel = this.doc.querySelector("#toolbox-panel-webconsole");
this.webconsolePanel.height = Services.prefs.getIntPref(SPLITCONSOLE_HEIGHT_PREF);
@ -906,6 +907,72 @@ Toolbox.prototype = {
}
},
/**
* Sets up keyboard navigation with and within the dev tools toolbar.
*/
_setToolbarKeyboardNavigation() {
let toolbar = this.doc.querySelector(".devtools-tabbar");
// Set and track aria-activedescendant to indicate which control is
// currently focused within the toolbar (for accessibility purposes).
toolbar.addEventListener("focus", event => {
let { target, rangeParent } = event;
let control, controlID = toolbar.getAttribute("aria-activedescendant");
if (controlID) {
control = this.doc.getElementById(controlID);
}
if (rangeParent || !control) {
// If range parent is present, the focused is moved within the toolbar,
// simply updating aria-activedescendant. Or if aria-activedescendant is
// not available, set it to target.
toolbar.setAttribute("aria-activedescendant", target.id);
} else {
// When range parent is not present, we focused into the toolbar, move
// focus to current aria-activedescendant.
event.preventDefault();
control.focus();
}
}, true)
toolbar.addEventListener("keypress", event => {
let { key, target } = event;
let win = this.doc.defaultView;
let elm, type;
if (key === "Tab") {
// Tabbing when toolbar or its contents are focused should move focus to
// next/previous focusable element relative to toolbar itself.
if (event.shiftKey) {
elm = toolbar;
type = Services.focus.MOVEFOCUS_BACKWARD;
} else {
// To move focus to next element following the toolbar, relative
// element needs to be the last element in its subtree.
let last = toolbar.lastChild;
while (last && last.lastChild) {
last = last.lastChild;
}
elm = last;
type = Services.focus.MOVEFOCUS_FORWARD;
}
} else if (key === "ArrowLeft") {
// Using left arrow key inside toolbar should move focus to previous
// toolbar control.
elm = target;
type = Services.focus.MOVEFOCUS_BACKWARD;
} else if (key === "ArrowRight") {
// Using right arrow key inside toolbar should move focus to next
// toolbar control.
elm = target;
type = Services.focus.MOVEFOCUS_FORWARD;
} else {
// Ignore all other keys.
return;
}
event.preventDefault();
Services.focus.moveFocus(win, elm, type, 0);
});
},
/**
* Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
*/

View File

@ -10,6 +10,7 @@ const {Cu, Ci} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const Services = require("Services");
const promise = require("promise");
const FocusManager = Services.focus;
const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
@ -72,6 +73,7 @@ HTMLBreadcrumbs.prototype = {
this.container.addEventListener("keypress", this, true);
this.container.addEventListener("mouseover", this, true);
this.container.addEventListener("mouseleave", this, true);
this.container.addEventListener("focus", this, true);
// We will save a list of already displayed nodes in this array.
this.nodeHierarchy = [];
@ -290,6 +292,24 @@ HTMLBreadcrumbs.prototype = {
this.handleMouseOver(event);
} else if (event.type == "mouseleave") {
this.handleMouseLeave(event);
} else if (event.type == "focus") {
this.handleFocus(event);
}
},
/**
* Focus event handler. When breadcrumbs container gets focus, if there is an
* already selected breadcrumb, move focus to it.
* @param {DOMEvent} event.
*/
handleFocus: function(event) {
let control = this.container.querySelector(
".breadcrumbs-widget-item[checked]");
if (control && control !== event.target) {
// If we already have a selected breadcrumb and focus target is not it,
// move focus to selected breadcrumb.
event.preventDefault();
control.focus();
}
},
@ -379,6 +399,26 @@ HTMLBreadcrumbs.prototype = {
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
});
break;
case this.chromeWin.KeyEvent.DOM_VK_TAB:
// Tabbing when breadcrumbs or its contents are focused should move
// focus to next/previous focusable element relative to breadcrumbs
// themselves.
let elm, type;
if (event.shiftKey) {
elm = this.container;
type = FocusManager.MOVEFOCUS_BACKWARD;
} else {
// To move focus to next element following the breadcrumbs, relative
// element needs to be the last element in breadcrumbs' subtree.
let last = this.container.lastChild;
while (last && last.lastChild) {
last = last.lastChild;
}
elm = last;
type = FocusManager.MOVEFOCUS_FORWARD;
}
FocusManager.moveFocus(this.chromeWin, elm, type, 0);
break;
}
return navigate.then(node => this.navigateTo(node));
@ -403,6 +443,7 @@ HTMLBreadcrumbs.prototype = {
this.container.removeEventListener("keypress", this, true);
this.container.removeEventListener("mouseover", this, true);
this.container.removeEventListener("mouseleave", this, true);
this.container.removeEventListener("focus", this, true);
this.empty();
this.separators.remove();

View File

@ -288,6 +288,12 @@ SelectorAutocompleter.prototype = {
this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
this.searchBox.value = this.searchPopup.selectedItem.label;
this.hidePopup();
} else if (!this.searchPopup.isOpen && event.keyCode === event.DOM_VK_TAB) {
// When tab is pressed with focus on searchbox and closed popup,
// do not prevent the default to avoid a keyboard trap and move focus
// to next/previous element.
this.emit("processing-done");
return;
}
break;

View File

@ -43,6 +43,8 @@ support-files =
[browser_inspector_breadcrumbs.js]
[browser_inspector_breadcrumbs_highlight_hover.js]
[browser_inspector_breadcrumbs_keybinding.js]
[browser_inspector_breadcrumbs_keyboard_trap.js]
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_inspector_breadcrumbs_menu.js]
[browser_inspector_breadcrumbs_mutations.js]
[browser_inspector_delete-selected-node-01.js]
@ -121,6 +123,7 @@ skip-if = (e10s && debug) # Bug 1250058 - Docshell leak on debug e10s
[browser_inspector_search-05.js]
[browser_inspector_search-06.js]
[browser_inspector_search-07.js]
[browser_inspector_search_keyboard_trap.js]
[browser_inspector_search-reserved.js]
[browser_inspector_search-selection.js]
[browser_inspector_select-docshell.js]

View File

@ -0,0 +1,79 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test ability to tab to and away from breadcrumbs using keyboard.
const TEST_URL = URL_ROOT + "doc_inspector_breadcrumbs.html";
/**
* Test data has the format of:
* {
* desc {String} description for better logging
* focused {Boolean} flag, indicating if breadcrumbs contain focus
* key {String} key event's key
* options {?Object} optional event data such as shiftKey, etc
* }
*/
const TEST_DATA = [
{
desc: "Move the focus away from breadcrumbs to a next focusable element",
focused: false,
key: "VK_TAB",
options: { }
},
{
desc: "Move the focus back to the breadcrumbs",
focused: true,
key: "VK_TAB",
options: { shiftKey: true }
},
{
desc: "Move the focus back away from breadcrumbs to a previous focusable element",
focused: false,
key: "VK_TAB",
options: { shiftKey: true }
},
{
desc: "Move the focus back to the breadcrumbs",
focused: true,
key: "VK_TAB",
options: { }
}
];
add_task(function*() {
let { toolbox, inspector } = yield openInspectorForURL(TEST_URL);
let doc = inspector.panelDoc;
yield selectNode("#i2", inspector);
info("Clicking on the corresponding breadcrumbs node to focus it");
let container = doc.getElementById("inspector-breadcrumbs");
let button = container.querySelector("button[checked]");
let onHighlight = toolbox.once("node-highlight");
button.click();
yield onHighlight;
// Ensure a breadcrumb is focused.
is(doc.activeElement, button, "Focus is on selected breadcrumb");
for (let { desc, focused, key, options } of TEST_DATA) {
info(desc);
let onUpdated;
if (!focused) {
onUpdated = inspector.once("breadcrumbs-navigation-cancelled");
}
EventUtils.synthesizeKey(key, options);
if (focused) {
is(doc.activeElement, button, "Focus is on selected breadcrumb");
} else {
yield onUpdated;
ok(!containsFocus(doc, container), "Focus is outside of breadcrumbs");
}
}
});

View File

@ -0,0 +1,93 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test ability to tab to and away from inspector search using keyboard.
const TEST_URL = URL_ROOT + "doc_inspector_search.html";
/**
* Test data has the format of:
* {
* desc {String} description for better logging
* focused {Boolean} flag, indicating if search box contains focus
* keys: {Array} list of keys that include key code and optional
* event data (shiftKey, etc)
* }
*
*/
const TEST_DATA = [
{
desc: "Move focus to a next focusable element",
focused: false,
keys: [
{
key: "VK_TAB",
options: { }
}
]
},
{
desc: "Move focus back to searchbox",
focused: true,
keys: [
{
key: "VK_TAB",
options: { shiftKey: true }
}
]
},
{
desc: "Open popup and then tab away (2 times) to the a next focusable element",
focused: false,
keys: [
{
key: "d",
options: { }
},
{
key: "VK_TAB",
options: { }
},
{
key: "VK_TAB",
options: { }
}
]
},
{
desc: "Move focus back to searchbox",
focused: true,
keys: [
{
key: "VK_TAB",
options: { shiftKey: true }
}
]
}
];
add_task(function*() {
let { inspector } = yield openInspectorForURL(TEST_URL);
let { searchBox } = inspector;
let doc = inspector.panelDoc;
yield selectNode("#b1", inspector);
yield focusSearchBoxUsingShortcut(inspector.panelWin);
// Ensure a searchbox is focused.
ok(containsFocus(doc, searchBox), "Focus is in a searchbox");
for (let { desc, focused, keys } of TEST_DATA) {
info(desc);
for (let { key, options } of keys) {
let done = !focused ?
inspector.searchSuggestions.once("processing-done") : Promise.resolve();
EventUtils.synthesizeKey(key, options);
yield done;
}
is(containsFocus(doc, searchBox), focused, "Focus is set correctly");
}
});

View File

@ -655,3 +655,20 @@ function waitForClipboard(setup, expected) {
SimpleTest.waitForClipboard(expected, setup, def.resolve, def.reject);
return def.promise;
}
/**
* Checks if document's active element is within the given element.
* @param {HTMLDocument} doc document with active element in question
* @param {DOMNode} container element tested on focus containment
* @return {Boolean}
*/
function containsFocus(doc, container) {
let elm = doc.activeElement;
while (elm) {
if (elm === container) {
return true;
}
elm = elm.parentNode;
}
return false;
}

View File

@ -605,6 +605,7 @@
#toolbox-controls > toolbarbutton,
#toolbox-dock-buttons > toolbarbutton {
-moz-appearance: none;
-moz-user-focus: normal;
border: none;
margin: 0 4px;
min-width: 16px;
@ -691,6 +692,7 @@
margin: 0;
width: 32px;
position: relative;
-moz-user-focus: normal;
}
.command-button:hover {
@ -812,6 +814,7 @@
border-width: 0;
-moz-border-start-width: 1px;
-moz-box-align: center;
-moz-user-focus: normal;
}
.theme-dark .devtools-tab {

View File

@ -839,7 +839,7 @@ ManifestHelper.prototype = {
_localeProp: function(aProp) {
if (this._localeRoot[aProp] != undefined)
return this._localeRoot[aProp];
return this._manifest[aProp];
return (aProp in this._manifest) ? this._manifest[aProp] : undefined;
},
get name() {

View File

@ -3875,6 +3875,14 @@ nsDOMWindowUtils::SetNextPaintSyncId(int32_t aSyncId)
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::RespectDisplayPortSuppression(bool aEnabled)
{
nsCOMPtr<nsIPresShell> shell(GetPresShell());
APZCCallbackHelper::RespectDisplayPortSuppression(aEnabled, shell);
return NS_OK;
}
NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)

View File

@ -34,7 +34,7 @@
GK_ATOM(_empty, "")
GK_ATOM(moz, "_moz")
GK_ATOM(mozframetype, "mozframetype")
GK_ATOM(_moz_abspos, "_moz_activated")
GK_ATOM(_moz_abspos, "_moz_abspos")
GK_ATOM(_moz_activated, "_moz_activated")
GK_ATOM(_moz_resizing, "_moz_resizing")
GK_ATOM(mozallowfullscreen, "mozallowfullscreen")
@ -2003,7 +2003,6 @@ GK_ATOM(scrollFrame, "ScrollFrame")
GK_ATOM(scrollbarFrame, "ScrollbarFrame")
GK_ATOM(sequenceFrame, "SequenceFrame")
GK_ATOM(sliderFrame, "sliderFrame")
GK_ATOM(summaryFrame, "SummaryFrame")
GK_ATOM(tableCellFrame, "TableCellFrame")
GK_ATOM(tableColFrame, "TableColFrame")
GK_ATOM(tableColGroupFrame, "TableColGroupFrame")

View File

@ -40,8 +40,8 @@ function testCharacterData(aNode, aText)
is(aNode.length, aText.length, "Text length should match");
is(aNode.data, aText, "Text content should match");
is(aNode.nodeValue, aText, "Check nodeValue");
is(aNode.localName, null, "Check localName")
is(aNode.namespaceURI, null, "Check namespaceURI");
is(aNode.localName, undefined, "Check localName")
is(aNode.namespaceURI, undefined, "Check namespaceURI");
}
function testComment(aText)
@ -90,8 +90,8 @@ function testPI(aTarget, aData, aShouldSucceed, aReason)
is(pi.data, aData, "Check data");
is(pi.nodeName, aTarget, "Check nodeName");
is(pi.nodeValue, aData, "Check nodeValue");
is(pi.localName, null, "Check localName")
is(pi.namespaceURI, null, "Check namespaceURI");
is(pi.localName, undefined, "Check localName")
is(pi.namespaceURI, undefined, "Check namespaceURI");
is(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE, "Check nodeType");

View File

@ -64,8 +64,8 @@ function testCharacterData(aNode, aText)
is(aNode.length, aText.length, "Text length should match");
is(aNode.data, aText, "Text content should match");
is(aNode.nodeValue, aText, "Check nodeValue");
is(aNode.localName, null, "Check localName")
is(aNode.namespaceURI, null, "Check namespaceURI");
is(aNode.localName, undefined, "Check localName")
is(aNode.namespaceURI, undefined, "Check namespaceURI");
}
function testComment(aText)
@ -134,8 +134,8 @@ function testPI(aTarget, aData, aShouldSucceed, aReason)
is(pi.data, aData, "Check data");
is(pi.nodeName, aTarget, "Check nodeName");
is(pi.nodeValue, aData, "Check nodeValue");
is(pi.localName, null, "Check localName")
is(pi.namespaceURI, null, "Check namespaceURI");
is(pi.localName, undefined, "Check localName")
is(pi.namespaceURI, undefined, "Check namespaceURI");
is(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE, "Check nodeType");

View File

@ -170,12 +170,17 @@ function createDrawFunc(canvas) {
}
gl.viewport(0, 0, canvas.width, canvas.height);
checkGLError(prefix + "[viewport]");
preDraw(prefix);
checkGLError(prefix + "[predraw]");
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
checkGLError(prefix + "[drawarrays]");
postDraw(prefix);
checkGLError(prefix + "[postdraw]");
if (needCommitFrame) {
gl.commit();
checkGLError(prefix + "[commit]");
}
checkGLError(prefix);
};

View File

@ -131,7 +131,7 @@ CreateDirectoryTaskChild::HandlerCallback()
void
CreateDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION);
}
/**
@ -216,7 +216,7 @@ CreateDirectoryTaskParent::IOWork()
void
CreateDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION);
}
} // namespace dom

View File

@ -11,8 +11,6 @@
#include "nsAutoPtr.h"
#include "mozilla/ErrorResult.h"
#define CREATE_DIRECTORY_TASK_PERMISSION "create"
namespace mozilla {
namespace dom {

View File

@ -5,8 +5,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CreateFileTask.h"
#include "CreateDirectoryTask.h"
#include "RemoveTask.h"
#include <algorithm>
@ -27,10 +25,10 @@
#define GET_PERMISSION_ACCESS_TYPE(aAccess) \
if (mReplace) { \
aAccess.AssignLiteral(REMOVE_TASK_PERMISSION); \
aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION); \
return; \
} \
aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION);
namespace mozilla {
namespace dom {

View File

@ -11,6 +11,7 @@
#include "FileSystemPermissionRequest.h"
#include "GetDirectoryListingTask.h"
#include "GetFileOrDirectoryTask.h"
#include "GetFilesTask.h"
#include "RemoveTask.h"
#include "nsCharSeparatedTokenizer.h"
@ -446,6 +447,27 @@ Directory::GetFilesAndDirectories(ErrorResult& aRv)
return task->GetPromise();
}
already_AddRefed<Promise>
Directory::GetFiles(bool aRecursiveFlag, ErrorResult& aRv)
{
ErrorResult rv;
RefPtr<FileSystemBase> fs = GetFileSystem(rv);
if (NS_WARN_IF(rv.Failed())) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
RefPtr<GetFilesTaskChild> task =
GetFilesTaskChild::Create(fs, mFile, aRecursiveFlag, rv);
if (NS_WARN_IF(rv.Failed())) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}
void
Directory::SetContentFilters(const nsAString& aFilters)
{

View File

@ -112,6 +112,9 @@ public:
already_AddRefed<Promise>
GetFilesAndDirectories(ErrorResult& aRv);
already_AddRefed<Promise>
GetFiles(bool aRecursiveFlag, ErrorResult& aRv);
// =========== End WebIDL bindings.============
/**

View File

@ -53,6 +53,7 @@ FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFiles)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)
default: {

View File

@ -20,6 +20,10 @@ class FileSystemBase;
class FileSystemParams;
class PBlobParent;
#define DIRECTORY_READ_PERMISSION "read"
#define DIRECTORY_WRITE_PERMISSION "write"
#define DIRECTORY_CREATE_PERMISSION "create"
/*
* The base class to implement a Task class.
* The file system operations can only be performed in the parent process. In

View File

@ -18,8 +18,6 @@
#include "nsIFile.h"
#include "nsStringGlue.h"
#define GET_DIRECTORY_LISTING_PERMISSION "read"
namespace mozilla {
namespace dom {
@ -206,7 +204,7 @@ GetDirectoryListingTaskChild::HandlerCallback()
void
GetDirectoryListingTaskChild::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
}
/**
@ -394,7 +392,7 @@ GetDirectoryListingTaskParent::IOWork()
void
GetDirectoryListingTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
}
} // namespace dom

View File

@ -17,8 +17,6 @@
#include "nsIFile.h"
#include "nsStringGlue.h"
#define GET_FILE_OR_DIRECTORY_PERMISSION "read"
namespace mozilla {
namespace dom {
@ -171,7 +169,7 @@ GetFileOrDirectoryTaskChild::HandlerCallback()
void
GetFileOrDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
}
/**
@ -298,7 +296,7 @@ GetFileOrDirectoryTaskParent::IOWork()
void
GetFileOrDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
}
} // namespace dom

View File

@ -0,0 +1,359 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GetFilesTask.h"
#include "HTMLSplitOnSpacesTokenizer.h"
#include "js/Value.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/PFileSystemParams.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
namespace mozilla {
namespace dom {
/**
* GetFilesTaskChild
*/
/* static */ already_AddRefed<GetFilesTaskChild>
GetFilesTaskChild::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
bool aRecursiveFlag,
ErrorResult& aRv)
{
MOZ_ASSERT(aFileSystem);
aFileSystem->AssertIsOnOwningThread();
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetParentObject());
if (NS_WARN_IF(!globalObject)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<GetFilesTaskChild> task =
new GetFilesTaskChild(aFileSystem, aTargetPath, aRecursiveFlag);
// aTargetPath can be null. In this case SetError will be called.
task->mPromise = Promise::Create(globalObject, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
GetFilesTaskChild::GetFilesTaskChild(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
bool aRecursiveFlag)
: FileSystemTaskChildBase(aFileSystem)
, mTargetPath(aTargetPath)
, mRecursiveFlag(aRecursiveFlag)
{
MOZ_ASSERT(aFileSystem);
aFileSystem->AssertIsOnOwningThread();
}
GetFilesTaskChild::~GetFilesTaskChild()
{
mFileSystem->AssertIsOnOwningThread();
}
already_AddRefed<Promise>
GetFilesTaskChild::GetPromise()
{
mFileSystem->AssertIsOnOwningThread();
return RefPtr<Promise>(mPromise).forget();
}
FileSystemParams
GetFilesTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const
{
mFileSystem->AssertIsOnOwningThread();
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemGetFilesParams();
}
return FileSystemGetFilesParams(aSerializedDOMPath, path, mRecursiveFlag);
}
void
GetFilesTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
{
mFileSystem->AssertIsOnOwningThread();
MOZ_ASSERT(aValue.type() ==
FileSystemResponseValue::TFileSystemFilesResponse);
FileSystemFilesResponse r = aValue;
if (!mTargetData.SetLength(r.data().Length(), mozilla::fallible_t())) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
for (uint32_t i = 0; i < r.data().Length(); ++i) {
const FileSystemFileResponse& data = r.data()[i];
mTargetData[i] = data.realPath();
}
}
void
GetFilesTaskChild::HandlerCallback()
{
mFileSystem->AssertIsOnOwningThread();
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
mPromise = nullptr;
return;
}
size_t count = mTargetData.Length();
Sequence<RefPtr<File>> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
nsCOMPtr<nsIFile> path;
NS_ConvertUTF16toUTF8 fullPath(mTargetData[i]);
nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
mPromise = nullptr;
return;
}
#ifdef DEBUG
nsCOMPtr<nsIFile> rootPath;
rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
mPromise = nullptr;
return;
}
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
#endif
RefPtr<File> file =
File::CreateFromFile(mFileSystem->GetParentObject(), path);
MOZ_ASSERT(file);
listing[i] = file;
}
mPromise->MaybeResolve(listing);
mPromise = nullptr;
}
void
GetFilesTaskChild::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("read");
}
/**
* GetFilesTaskParent
*/
/* static */ already_AddRefed<GetFilesTaskParent>
GetFilesTaskParent::Create(FileSystemBase* aFileSystem,
const FileSystemGetFilesParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
RefPtr<GetFilesTaskParent> task =
new GetFilesTaskParent(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
GetFilesTaskParent::GetFilesTaskParent(FileSystemBase* aFileSystem,
const FileSystemGetFilesParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
, mRecursiveFlag(aParam.recursiveFlag())
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
}
FileSystemResponseValue
GetFilesTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
InfallibleTArray<PBlobParent*> blobs;
FallibleTArray<FileSystemFileResponse> inputs;
if (!inputs.SetLength(mTargetData.Length(), mozilla::fallible_t())) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
FileSystemFilesResponse response;
return response;
}
for (unsigned i = 0; i < mTargetData.Length(); i++) {
FileSystemFileResponse fileData;
fileData.realPath() = mTargetData[i];
inputs[i] = fileData;
}
FileSystemFilesResponse response;
response.data().SwapElements(inputs);
return response;
}
nsresult
GetFilesTaskParent::IOWork()
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(!NS_IsMainThread(), "Only call on I/O thread!");
if (mFileSystem->IsShutdown()) {
return NS_ERROR_FAILURE;
}
bool exists;
nsresult rv = mTargetPath->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
return NS_OK;
}
// Get isDirectory.
rv = ExploreDirectory(mTargetPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
GetFilesTaskParent::ExploreDirectory(nsIFile* aPath)
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
MOZ_ASSERT(aPath);
bool isDir;
nsresult rv = aPath->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!isDir) {
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
}
nsCOMPtr<nsISimpleEnumerator> entries;
rv = aPath->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (;;) {
bool hasMore = false;
if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) {
break;
}
nsCOMPtr<nsISupports> supp;
if (NS_WARN_IF(NS_FAILED(entries->GetNext(getter_AddRefs(supp))))) {
break;
}
nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
MOZ_ASSERT(currFile);
bool isLink, isSpecial, isFile;
if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
isLink || isSpecial) {
continue;
}
if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
NS_FAILED(currFile->IsDirectory(&isDir))) ||
!(isFile || isDir)) {
continue;
}
if (isFile) {
nsAutoString path;
if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
continue;
}
if (!mTargetData.AppendElement(path, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
continue;
}
MOZ_ASSERT(isDir);
if (!mRecursiveFlag) {
continue;
}
// Recursive.
rv = ExploreDirectory(currFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
void
GetFilesTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,99 @@
/* -*- 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/. */
#ifndef mozilla_dom_GetFilesTask_h
#define mozilla_dom_GetFilesTask_h
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/ErrorResult.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace dom {
class BlobImpl;
class GetFilesTaskChild final : public FileSystemTaskChildBase
{
public:
static already_AddRefed<GetFilesTaskChild>
Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
bool aRecursiveFlag,
ErrorResult& aRv);
virtual
~GetFilesTaskChild();
already_AddRefed<Promise>
GetPromise();
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
private:
// If aDirectoryOnly is set, we should ensure that the target is a directory.
GetFilesTaskChild(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
bool aRecursiveFlag);
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
virtual void
HandlerCallback() override;
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
bool mRecursiveFlag;
// We store the fullpath of Files.
FallibleTArray<nsString> mTargetData;
};
class GetFilesTaskParent final : public FileSystemTaskParentBase
{
public:
static already_AddRefed<GetFilesTaskParent>
Create(FileSystemBase* aFileSystem,
const FileSystemGetFilesParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
private:
GetFilesTaskParent(FileSystemBase* aFileSystem,
const FileSystemGetFilesParams& aParam,
FileSystemRequestParent* aParent);
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual nsresult
IOWork() override;
nsresult
ExploreDirectory(nsIFile* aPath);
nsCOMPtr<nsIFile> mTargetPath;
bool mRecursiveFlag;
// We store the fullpath of Files.
FallibleTArray<nsString> mTargetData;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_GetFilesTask_h

View File

@ -44,6 +44,13 @@ struct FileSystemGetDirectoryListingParams
nsString filters;
};
struct FileSystemGetFilesParams
{
nsString filesystem;
nsString realPath;
bool recursiveFlag;
};
struct FileSystemGetFileOrDirectoryParams
{
nsString filesystem;
@ -64,6 +71,7 @@ union FileSystemParams
FileSystemCreateDirectoryParams;
FileSystemCreateFileParams;
FileSystemGetDirectoryListingParams;
FileSystemGetFilesParams;
FileSystemGetFileOrDirectoryParams;
FileSystemRemoveParams;
};

View File

@ -43,6 +43,11 @@ struct FileSystemDirectoryListingResponse
FileSystemDirectoryListingResponseData[] data;
};
struct FileSystemFilesResponse
{
FileSystemFileResponse[] data;
};
struct FileSystemErrorResponse
{
nsresult error;
@ -59,6 +64,7 @@ union FileSystemResponseValue
FileSystemDirectoryResponse;
FileSystemDirectoryListingResponse;
FileSystemFileResponse;
FileSystemFilesResponse;
FileSystemErrorResponse;
};

View File

@ -142,7 +142,7 @@ RemoveTaskChild::HandlerCallback()
void
RemoveTaskChild::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION);
}
/**
@ -252,7 +252,7 @@ RemoveTaskParent::IOWork()
void
RemoveTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION);
}
} // namespace dom

View File

@ -11,8 +11,6 @@
#include "nsAutoPtr.h"
#include "mozilla/ErrorResult.h"
#define REMOVE_TASK_PERMISSION "write"
namespace mozilla {
namespace dom {

View File

@ -28,6 +28,7 @@ UNIFIED_SOURCES += [
'FileSystemUtils.cpp',
'GetDirectoryListingTask.cpp',
'GetFileOrDirectoryTask.cpp',
'GetFilesTask.cpp',
'OSFileSystem.cpp',
'RemoveTask.cpp',
]

View File

@ -0,0 +1,75 @@
function test_basic(aDirectory, aNext) {
ok(aDirectory, "Directory exists.");
ok(aDirectory instanceof Directory, "We have a directory.");
is(aDirectory.name, '/', "directory.name must be '/'");
is(aDirectory.path, '/', "directory.path must be '/'");
aNext();
}
function test_getFilesAndDirectories(aDirectory, aRecursive, aNext) {
function checkSubDir(dir) {
return dir.getFilesAndDirectories().then(
function(data) {
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path.");
is(data[i].path,dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname");
}
}
}
);
}
aDirectory.getFilesAndDirectories().then(
function(data) {
ok(data.length, "We should have some data.");
var promises = [];
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories: " + data[i].name);
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
is(data[i].path, '/' + data[i].name, "Subdirectory path should be called '/' + leafname");
if (aRecursive) {
promises.push(checkSubDir(data[i]));
}
}
}
return Promise.all(promises);
},
function() {
ok(false, "Something when wrong");
}
).then(aNext);
}
function test_getFiles(aDirectory, aRecursive, aNext) {
aDirectory.getFiles(aRecursive).then(
function(data) {
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File, "File: " + data[i].name);
}
},
function() {
ok(false, "Something when wrong");
}
).then(aNext);
}
function test_getFiles_recursiveComparison(aDirectory, aNext) {
aDirectory.getFiles(true).then(function(data) {
is(data.length, 2, "Only 2 files for this test.");
ok(data[0].name == 'foo.txt' || data[0].name == 'bar.txt', "First filename matches");
ok(data[1].name == 'foo.txt' || data[1].name == 'bar.txt', "Second filename matches");
}).then(function() {
return aDirectory.getFiles(false);
}).then(function(data) {
is(data.length, 1, "Only 1 file for this test.");
ok(data[0].name == 'foo.txt' || data[0].name == 'bar.txt', "First filename matches");
}).catch(function() {
ok(false, "Something when wrong");
}).then(aNext);
}

View File

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
filesystem_commons.js
script_fileList.js
worker_basic.js

View File

@ -1,22 +1,67 @@
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.importGlobalProperties(["File"]);
addMessageListener("dir.open", function (e) {
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get(e.path == 'root' ? 'ProfD' : e.path, Ci.nsIFile);
function createProfDFile() {
return Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get('ProfD', Ci.nsIFile);
}
function createRootFile() {
var testFile = createProfDFile();
// Let's go back to the root of the FileSystem
if (e.path == 'root') {
while (true) {
var parent = testFile.parent;
if (!parent) {
break;
}
testFile = parent;
while (true) {
var parent = testFile.parent;
if (!parent) {
break;
}
testFile = parent;
}
return testFile;
}
function createTestFile() {
var tmpFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get('TmpD', Ci.nsIFile)
tmpFile.append('dir-test');
tmpFile.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
var file1 = tmpFile.clone();
file1.append('foo.txt');
file1.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
var dir = tmpFile.clone();
dir.append('subdir');
dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
var file2 = dir.clone();
file2.append('bar.txt');
file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
return tmpFile;
}
addMessageListener("dir.open", function (e) {
var testFile;
switch (e.path) {
case 'ProfD':
testFile = createProfDFile();
break;
case 'root':
testFile = createRootFile();
break;
case 'test':
testFile = createTestFile();
break;
}
sendAsyncMessage("dir.opened", {

View File

@ -3,6 +3,7 @@
<head>
<title>Test for Directory API</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="filesystem_commons.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
@ -34,63 +35,20 @@ function create_fileList(aPath) {
script.sendAsyncMessage("dir.open", { path: aPath });
}
function test_basic() {
ok(directory, "Directory exists.");
ok(directory instanceof Directory, "We have a directory.");
is(directory.name, '/', "directory.name must be '/'");
is(directory.path, '/', "directory.path must be '/'");
next();
}
function checkSubDir(dir) {
return dir.getFilesAndDirectories().then(
function(data) {
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname: " + data[i].name);
isnot(data[i].path, '/', "Subdirectory path should be called with the leafname:" + data[i].path);
isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path.");
is(data[i].path,dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname");
}
}
}
);
}
function getFilesAndDirectories(aRecursive) {
directory.getFilesAndDirectories().then(
function(data) {
ok(data.length, "We should have some data.");
var promises = [];
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
is(data[i].path, '/' + data[i].name, "Subdirectory path should be called '/' + leafname");
if (aRecursive) {
promises.push(checkSubDir(data[i]));
}
}
}
return Promise.all(promises);
},
function() {
ok(false, "Something when wrong");
}
).then(next);
}
var tests = [
function() { create_fileList('ProfD') },
test_basic,
function() { getFilesAndDirectories(true) },
function() { test_basic(directory, next); },
function() { test_getFilesAndDirectories(directory, true, next); },
function() { test_getFiles(directory, false, next); },
function() { test_getFiles(directory, true, next); },
function() { create_fileList('root') },
test_basic,
function() { getFilesAndDirectories(false) },
function() { create_fileList('test') },
function() { test_getFiles_recursiveComparison(directory, next); },
function() { create_fileList('root'); },
function() { test_basic(directory, next); },
function() { test_getFilesAndDirectories(directory, false, next); },
function() { test_getFiles(directory, false, next); },
];
function next() {

View File

@ -1,3 +1,5 @@
importScripts('filesystem_commons.js');
function finish() {
postMessage({ type: 'finish' });
}
@ -14,43 +16,26 @@ function isnot(a, b, msg) {
ok(a != b, msg);
}
function checkSubDir(dir) {
return dir.getFilesAndDirectories().then(
function(data) {
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path.");
is(data[i].path,dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname");
}
}
}
);
var tests = [
function() { test_basic(directory, next); },
function() { test_getFilesAndDirectories(directory, true, next); },
function() { test_getFiles(directory, false, next); },
function() { test_getFiles(directory, true, next); },
];
function next() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
var directory;
onmessage = function(e) {
var directory = e.data;
ok(directory instanceof Directory, "This is a directory.");
directory.getFilesAndDirectories().then(
function(data) {
ok(data.length, "We should have some data.");
var promises = [];
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
is(data[i].path, '/' + data[i].name, "Subdirectory path should be called '/' + leafname");
promises.push(checkSubDir(data[i]));
}
}
return Promise.all(promises);
},
function() {
ok(false, "Something when wrong");
}
).then(finish);
directory = e.data;
next();
}

View File

@ -285,6 +285,9 @@ ImageDocument::OnPageShow(bool aPersisted,
mOriginalZoomLevel =
Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel();
}
RefPtr<ImageDocument> kungFuDeathGrip(this);
UpdateSizeFromLayout();
MediaDocument::OnPageShow(aPersisted, aDispatchStartTarget);
}

View File

@ -1,2 +0,0 @@
hg|https://dvcs.w3.org/hg/editing|editing

File diff suppressed because it is too large Load Diff

View File

@ -1,290 +0,0 @@
<!doctype html>
<title>Editing event tests</title>
<style>body { font-family: serif }</style>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div id=test></div>
<div id=log></div>
<script>
"use strict";
var div = document.querySelector("#test");
add_completion_callback(function() { div.parentNode.removeChild(div) });
function copyEvent(e) {
var ret = {};
ret.original = e;
["type", "target", "currentTarget", "eventPhase", "bubbles", "cancelable",
"defaultPrevented", "isTrusted", "command", "value"].forEach(function(k) {
ret[k] = e[k];
});
return ret;
}
var tests = [
{
name: "Simple editable div",
html: "<div contenteditable>foo<b>bar</b>baz</div>",
initRange: function(range) {
range.setStart(div.querySelector("b").firstChild, 0);
range.setEnd(div.querySelector("b"), 1);
},
target: function() { return div.firstChild },
command: "bold",
value: "",
},
{
name: "Editable b",
html: "foo<b contenteditable>bar</b>baz",
initRange: function(range) {
range.setStart(div.querySelector("b").firstChild, 0);
range.setEnd(div.querySelector("b"), 1);
},
target: function() { return div.querySelector("b") },
command: "bold",
value: "",
},
{
name: "No editable content",
html: "foo<b>bar</b>baz",
initRange: function(range) {
range.setStart(div.querySelector("b").firstChild, 0);
range.setEnd(div.querySelector("b"), 1);
},
target: function() { return null },
command: "bold",
value: "",
},
{
name: "Partially-selected editable content",
html: "foo<b contenteditable>bar</b>baz",
initRange: function(range) {
range.setStart(div.querySelector("b").firstChild, 0);
range.setEnd(div, 3);
},
target: function() { return null },
command: "bold",
value: "",
},
{
name: "Selection spans two editing hosts",
html: "<div contenteditable>foo</div><div contenteditable>bar</div>",
initRange: function(range) {
range.setStart(div.querySelector("div").firstChild, 2);
range.setEnd(div.querySelector("div + div").firstChild, 1);
},
target: function() { return null },
command: "bold",
value: "",
},
{
name: "Selection includes two editing hosts",
html: "foo<div contenteditable>bar</div>baz<div contenteditable>quz</div>qoz",
initRange: function(range) {
range.setStart(div.firstChild, 2);
range.setEnd(div.lastChild, 1);
},
target: function() { return null },
command: "bold",
value: "",
},
{
name: "Changing selection from handler",
html: "<div contenteditable>foo</div><div contenteditable>bar</div>",
initRange: function(range) {
range.setStart(div.querySelector("div").firstChild, 0);
range.setEnd(div.querySelector("div").firstChild, 3);
},
target: function() { return div.firstChild },
finalTarget: function() { return div.lastChild },
beforeInputAction: function() {
getSelection().removeAllRanges();
var range = document.createRange();
range.setStart(div.querySelector("div + div").firstChild, 0);
range.setEnd(div.querySelector("div + div").firstChild, 3);
getSelection().addRange(range);
},
command: "bold",
value: "",
},
];
var commandTests = {
backColor: ["green"],
createLink: ["http://www.w3.org/community/editing/"],
fontName: ["serif", "Helvetica"],
fontSize: ["6", "15px"],
foreColor: ["green"],
hiliteColor: ["green"],
italic: [],
removeFormat: [],
strikeThrough: [],
subscript: [],
superscript: [],
underline: [],
unlink: [],
delete: [],
formatBlock: ["p"],
forwardDelete: [],
indent: [],
insertHorizontalRule: ["id"],
insertHTML: ["<b>hi</b>"],
insertImage: ["http://example.com/some-image"],
insertLineBreak: [],
insertOrderedList: [],
insertParagraph: [],
insertText: ["abc"],
insertUnorderedList: [],
justifyCenter: [],
justifyFull: [],
justifyLeft: [],
justifyRight: [],
outdent: [],
redo: [],
selectAll: [],
styleWithCSS: [],
undo: [],
useCSS: [],
};
Object.keys(commandTests).forEach(function(command) {
commandTests[command] = ["", "quasit"].concat(commandTests[command]);
commandTests[command].forEach(function(value) {
tests.push({
name: "Command " + command + ", value " + format_value(value),
html: "<div contenteditable>foo<b>bar</b>baz</div>",
initRange: function(range) {
range.setStart(div.querySelector("b").firstChild, 0);
range.setEnd(div.querySelector("b"), 1);
},
target: function() {
return ["redo", "selectAll", "styleWithCSS", "undo", "useCSS"]
.indexOf(command) == -1 ? div.firstChild : null;
},
command: command,
value: value,
});
});
});
tests.forEach(function(obj) {
[true, false].forEach(function(cancel) {
// Kill all event handlers first
var newDiv = div.cloneNode(false);
div.parentNode.insertBefore(newDiv, div);
div.parentNode.removeChild(div);
div = newDiv;
div.innerHTML = obj.html;
var originalContents = div.cloneNode(true);
getSelection().removeAllRanges();
var range = document.createRange();
obj.initRange(range);
getSelection().addRange(range);
var target = obj.target();
var finalTarget = "finalTarget" in obj ? obj.finalTarget() : target;
var command = obj.command;
var value = obj.value;
var beforeInputEvents = [];
var inputEvents = [];
div.addEventListener("beforeinput", function(e) {
var copied = copyEvent(e);
copied.inputEventsLength = inputEvents.length;
beforeInputEvents.push(copied);
if (cancel) {
e.preventDefault();
}
if ("beforeInputAction" in obj) {
obj.beforeInputAction();
}
});
div.addEventListener("input", function(e) { inputEvents.push(copyEvent(e)) });
// Uncomment this code instead of the execCommand() to make all the
// tests pass, as a sanity check
//var e = new Event("beforeinput", {bubbles: true, cancelable: true});
//e.command = command;
//e.value = value;
//var ret = target ? target.dispatchEvent(e) : false;
//if (ret) {
// var e = new Event("input", {bubbles: true});
// e.command = command;
// e.value = value;
// finalTarget.dispatchEvent(e);
//}
var exception = null;
try {
document.execCommand(command, false, value);
} catch(e) {
exception = e;
}
test(function() {
assert_equals(exception, null, "Unexpected exception");
}, obj.name + ": execCommand() must not throw, "
+ (cancel ? "canceled" : "uncanceled"));
test(function() {
assert_equals(beforeInputEvents.length, target ? 1 : 0,
"number of beforeinput events fired");
if (beforeInputEvents.length == 0) {
assert_equals(inputEvents.length, 0, "number of input events fired");
return;
}
var e = beforeInputEvents[0];
assert_equals(e.inputEventsLength, 0, "number of input events fired");
assert_equals(e.type, "beforeinput", "event.type");
assert_equals(e.target, target, "event.target");
assert_equals(e.currentTarget, div, "event.currentTarget");
assert_equals(e.eventPhase, Event.BUBBLING_PHASE, "event.eventPhase");
assert_equals(e.bubbles, true, "event.bubbles");
assert_equals(e.cancelable, true, "event.cancelable");
assert_equals(e.defaultPrevented, false, "event.defaultPrevented");
assert_equals(e.command, command, "e.command");
assert_equals(e.value, value, "e.value");
assert_own_property(window, "EditingBeforeInputEvent",
"window.EditingBeforeInputEvent must exist");
assert_equals(Object.getPrototypeOf(e.original),
EditingBeforeInputEvent.prototype,
"event prototype");
assert_true(originalContents.isEqualNode(div),
"div contents not yet changed");
assert_equals(e.isTrusted, true, "event.isTrusted");
}, obj.name + ": beforeinput event, " + (cancel ? "canceled" : "uncanceled"));
test(function() {
assert_equals(inputEvents.length, target && !cancel ? 1 : 0,
"number of input events fired");
if (!target || cancel) {
assert_true(originalContents.isEqualNode(div),
"div contents must not be changed");
return;
}
var e = inputEvents[0];
assert_equals(e.type, "input", "event.type");
assert_equals(e.target, finalTarget, "event.target");
assert_equals(e.currentTarget, div, "event.currentTarget");
assert_equals(e.eventPhase, Event.BUBBLING_PHASE, "event.eventPhase");
assert_equals(e.bubbles, true, "event.bubbles");
assert_equals(e.cancelable, false, "event.cancelable");
assert_equals(e.defaultPrevented, false, "event.defaultPrevented");
assert_equals(e.command, command, "e.command");
assert_equals(e.value, value, "e.value");
assert_own_property(window, "EditingInputEvent",
"window.EditingInputEvent must exist");
assert_equals(Object.getPrototypeOf(e.original),
EditingInputEvent.prototype,
"event prototype");
assert_equals(e.isTrusted, true, "event.isTrusted");
}, obj.name + ": input event, " + (cancel ? "canceled" : "uncanceled"));
});
});
// Thanks, Gecko.
document.body.bgColor = "";
</script>

View File

@ -1,52 +0,0 @@
<!doctype html>
<meta charset=utf-8>
<link rel=stylesheet href=../css/reset.css>
<title>HTML editing conformance tests</title>
<p>See the <a href=editing.html#tests>Tests</a> section of the specification
for documentation.
<p id=timing></p>
<div id=log></div>
<div id=test-container></div>
<script src=../implementation.js></script>
<script>var testsJsLibraryOnly = true</script>
<script src=../tests.js></script>
<script src=data.js></script>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
"use strict";
if (W3CTest.runner) {
W3CTest.runner.requestLongerTimeout(2);
}
runTests();
function runTests() {
var startTime = Date.now();
// Make document.body.innerHTML more tidy by removing unnecessary things.
// We can't remove the testharness.js script, because at the time of this
// writing, for some reason that stops it from adding appropriate CSS.
[].forEach.call(document.querySelectorAll("script"), function(node) {
if (!/testharness\.js$/.test(node.src)) {
node.parentNode.removeChild(node);
}
});
browserTests.forEach(runConformanceTest);
document.getElementById("test-container").parentNode
.removeChild(document.getElementById("test-container"));
var elapsed = Math.round(Date.now() - startTime)/1000;
document.getElementById("timing").textContent =
"Time elapsed: " + Math.floor(elapsed/60) + ":"
+ ((elapsed % 60) < 10 ? "0" : "")
+ (elapsed % 60).toFixed(3) + " min.";
}
</script>

View File

@ -1,27 +0,0 @@
/* Make sure various CSS values are what are expected, so that tests work
* right. */
body { font-family: serif }
/* http://www.w3.org/Bugs/Public/show_bug.cgi?id=12154
* https://bugzilla.mozilla.org/show_bug.cgi?id=589124
* https://bugs.webkit.org/show_bug.cgi?id=56400 */
b, strong { font-weight: bold }
.bold { font-weight: bold }
.notbold { font-weight: normal }
.underline { text-decoration: underline }
.line-through { text-decoration: line-through }
.underline-and-line-through { text-decoration: underline line-through }
#purple { color: purple }
/* https://bugs.webkit.org/show_bug.cgi?id=56670 */
dfn { font-style: italic }
/* Opera has weird default blockquote style */
blockquote { margin: 1em 40px }
/* Some tests assume links are blue, for the sake of argument, but they aren't
* blue in any browser. And :visited definitely isn't blue, except in engines
* like Gecko that lie.
*
* This should really be #00e, probably. See:
* http://www.w3.org/Bugs/Public/show_bug.cgi?id=13330 */
:link, :visited { color: blue }
/* http://www.w3.org/Bugs/Public/show_bug.cgi?id=14066
* https://bugs.webkit.org/show_bug.cgi?id=68392 */
quasit { text-align: inherit }

File diff suppressed because it is too large Load Diff

View File

@ -1,952 +0,0 @@
"use strict";
// TODO: iframes, contenteditable/designMode
// Everything is done in functions in this test harness, so we have to declare
// all the variables before use to make sure they can be reused.
var selection;
var testDiv, paras, detachedDiv, detachedPara1, detachedPara2,
foreignDoc, foreignPara1, foreignPara2, xmlDoc, xmlElement,
detachedXmlElement, detachedTextNode, foreignTextNode,
detachedForeignTextNode, xmlTextNode, detachedXmlTextNode,
processingInstruction, detachedProcessingInstruction, comment,
detachedComment, foreignComment, detachedForeignComment, xmlComment,
detachedXmlComment, docfrag, foreignDocfrag, xmlDocfrag, doctype,
foreignDoctype, xmlDoctype;
var testRanges, testPoints, testNodes;
function setupRangeTests() {
selection = getSelection();
testDiv = document.querySelector("#test");
if (testDiv) {
testDiv.parentNode.removeChild(testDiv);
}
testDiv = document.createElement("div");
testDiv.id = "test";
document.body.insertBefore(testDiv, document.body.firstChild);
// Test some diacritics, to make sure browsers are using code units here
// and not something like grapheme clusters.
testDiv.innerHTML = "<p id=a>A&#x308;b&#x308;c&#x308;d&#x308;e&#x308;f&#x308;g&#x308;h&#x308;\n"
+ "<p id=b style=display:none>Ijklmnop\n"
+ "<p id=c>Qrstuvwx"
+ "<p id=d style=display:none>Yzabcdef"
+ "<p id=e style=display:none>Ghijklmn";
paras = testDiv.querySelectorAll("p");
detachedDiv = document.createElement("div");
detachedPara1 = document.createElement("p");
detachedPara1.appendChild(document.createTextNode("Opqrstuv"));
detachedPara2 = document.createElement("p");
detachedPara2.appendChild(document.createTextNode("Wxyzabcd"));
detachedDiv.appendChild(detachedPara1);
detachedDiv.appendChild(detachedPara2);
// Opera doesn't automatically create a doctype for a new HTML document,
// contrary to spec. It also doesn't let you add doctypes to documents
// after the fact through any means I've tried. So foreignDoc in Opera
// will have no doctype, foreignDoctype will be null, and Opera will fail
// some tests somewhat mysteriously as a result.
foreignDoc = document.implementation.createHTMLDocument("");
foreignPara1 = foreignDoc.createElement("p");
foreignPara1.appendChild(foreignDoc.createTextNode("Efghijkl"));
foreignPara2 = foreignDoc.createElement("p");
foreignPara2.appendChild(foreignDoc.createTextNode("Mnopqrst"));
foreignDoc.body.appendChild(foreignPara1);
foreignDoc.body.appendChild(foreignPara2);
// Now we get to do really silly stuff, which nobody in the universe is
// ever going to actually do, but the spec defines behavior, so too bad.
// Testing is fun!
xmlDoctype = document.implementation.createDocumentType("qorflesnorf", "abcde", "x\"'y");
xmlDoc = document.implementation.createDocument(null, null, xmlDoctype);
detachedXmlElement = xmlDoc.createElement("everyone-hates-hyphenated-element-names");
detachedTextNode = document.createTextNode("Uvwxyzab");
detachedForeignTextNode = foreignDoc.createTextNode("Cdefghij");
detachedXmlTextNode = xmlDoc.createTextNode("Klmnopqr");
// PIs only exist in XML documents, so don't bother with document or
// foreignDoc.
detachedProcessingInstruction = xmlDoc.createProcessingInstruction("whippoorwill", "chirp chirp chirp");
detachedComment = document.createComment("Stuvwxyz");
// Hurrah, we finally got to "z" at the end!
detachedForeignComment = foreignDoc.createComment("אריה יהודה");
detachedXmlComment = xmlDoc.createComment("בן חיים אליעזר");
// We should also test with document fragments that actually contain stuff
// . . . but, maybe later.
docfrag = document.createDocumentFragment();
foreignDocfrag = foreignDoc.createDocumentFragment();
xmlDocfrag = xmlDoc.createDocumentFragment();
xmlElement = xmlDoc.createElement("igiveuponcreativenames");
xmlTextNode = xmlDoc.createTextNode("do re mi fa so la ti");
xmlElement.appendChild(xmlTextNode);
processingInstruction = xmlDoc.createProcessingInstruction("somePI", 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?');
xmlDoc.appendChild(xmlElement);
xmlDoc.appendChild(processingInstruction);
xmlComment = xmlDoc.createComment("I maliciously created a comment that will break incautious XML serializers, but Firefox threw an exception, so all I got was this lousy T-shirt");
xmlDoc.appendChild(xmlComment);
comment = document.createComment("Alphabet soup?");
testDiv.appendChild(comment);
foreignComment = foreignDoc.createComment('"Commenter" and "commentator" mean different things. I\'ve seen non-native speakers trip up on this.');
foreignDoc.appendChild(foreignComment);
foreignTextNode = foreignDoc.createTextNode("I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.");
foreignDoc.body.appendChild(foreignTextNode);
doctype = document.doctype;
foreignDoctype = foreignDoc.doctype;
testRanges = [
// Various ranges within the text node children of different
// paragraphs. All should be valid.
"[paras[0].firstChild, 0, paras[0].firstChild, 0]",
"[paras[0].firstChild, 0, paras[0].firstChild, 1]",
"[paras[0].firstChild, 2, paras[0].firstChild, 8]",
"[paras[0].firstChild, 2, paras[0].firstChild, 9]",
"[paras[1].firstChild, 0, paras[1].firstChild, 0]",
"[paras[1].firstChild, 0, paras[1].firstChild, 1]",
"[paras[1].firstChild, 2, paras[1].firstChild, 8]",
"[paras[1].firstChild, 2, paras[1].firstChild, 9]",
"[detachedPara1.firstChild, 0, detachedPara1.firstChild, 0]",
"[detachedPara1.firstChild, 0, detachedPara1.firstChild, 1]",
"[detachedPara1.firstChild, 2, detachedPara1.firstChild, 8]",
"[foreignPara1.firstChild, 0, foreignPara1.firstChild, 0]",
"[foreignPara1.firstChild, 0, foreignPara1.firstChild, 1]",
"[foreignPara1.firstChild, 2, foreignPara1.firstChild, 8]",
// Now try testing some elements, not just text nodes.
"[document.documentElement, 0, document.documentElement, 1]",
"[document.documentElement, 0, document.documentElement, 2]",
"[document.documentElement, 1, document.documentElement, 2]",
"[document.head, 1, document.head, 1]",
"[document.body, 0, document.body, 1]",
"[foreignDoc.documentElement, 0, foreignDoc.documentElement, 1]",
"[foreignDoc.head, 1, foreignDoc.head, 1]",
"[foreignDoc.body, 0, foreignDoc.body, 0]",
"[paras[0], 0, paras[0], 0]",
"[paras[0], 0, paras[0], 1]",
"[detachedPara1, 0, detachedPara1, 0]",
"[detachedPara1, 0, detachedPara1, 1]",
// Now try some ranges that span elements.
"[paras[0].firstChild, 0, paras[1].firstChild, 0]",
"[paras[0].firstChild, 0, paras[1].firstChild, 8]",
"[paras[0].firstChild, 3, paras[3], 1]",
// How about something that spans a node and its descendant?
"[paras[0], 0, paras[0].firstChild, 7]",
"[testDiv, 2, paras[4], 1]",
"[testDiv, 1, paras[2].firstChild, 5]",
"[document.documentElement, 1, document.body, 0]",
"[foreignDoc.documentElement, 1, foreignDoc.body, 0]",
// Then a few more interesting things just for good measure.
"[document, 0, document, 1]",
"[document, 0, document, 2]",
"[document, 1, document, 2]",
"[testDiv, 0, comment, 5]",
"[paras[2].firstChild, 4, comment, 2]",
"[paras[3], 1, comment, 8]",
"[foreignDoc, 0, foreignDoc, 0]",
"[foreignDoc, 1, foreignComment, 2]",
"[foreignDoc.body, 0, foreignTextNode, 36]",
"[xmlDoc, 0, xmlDoc, 0]",
// Opera 11 crashes if you extractContents() a range that ends at offset
// zero in a comment. Comment out this line to run the tests successfully.
"[xmlDoc, 1, xmlComment, 0]",
"[detachedTextNode, 0, detachedTextNode, 8]",
"[detachedForeignTextNode, 7, detachedForeignTextNode, 7]",
"[detachedForeignTextNode, 0, detachedForeignTextNode, 8]",
"[detachedXmlTextNode, 7, detachedXmlTextNode, 7]",
"[detachedXmlTextNode, 0, detachedXmlTextNode, 8]",
"[detachedComment, 3, detachedComment, 4]",
"[detachedComment, 5, detachedComment, 5]",
"[detachedForeignComment, 0, detachedForeignComment, 1]",
"[detachedForeignComment, 4, detachedForeignComment, 4]",
"[detachedXmlComment, 2, detachedXmlComment, 6]",
"[docfrag, 0, docfrag, 0]",
"[foreignDocfrag, 0, foreignDocfrag, 0]",
"[xmlDocfrag, 0, xmlDocfrag, 0]",
];
testPoints = [
// Various positions within the page, some invalid. Remember that
// paras[0] is visible, and paras[1] is display: none.
"[paras[0].firstChild, -1]",
"[paras[0].firstChild, 0]",
"[paras[0].firstChild, 1]",
"[paras[0].firstChild, 2]",
"[paras[0].firstChild, 8]",
"[paras[0].firstChild, 9]",
"[paras[0].firstChild, 10]",
"[paras[0].firstChild, 65535]",
"[paras[1].firstChild, -1]",
"[paras[1].firstChild, 0]",
"[paras[1].firstChild, 1]",
"[paras[1].firstChild, 2]",
"[paras[1].firstChild, 8]",
"[paras[1].firstChild, 9]",
"[paras[1].firstChild, 10]",
"[paras[1].firstChild, 65535]",
"[detachedPara1.firstChild, 0]",
"[detachedPara1.firstChild, 1]",
"[detachedPara1.firstChild, 8]",
"[detachedPara1.firstChild, 9]",
"[foreignPara1.firstChild, 0]",
"[foreignPara1.firstChild, 1]",
"[foreignPara1.firstChild, 8]",
"[foreignPara1.firstChild, 9]",
// Now try testing some elements, not just text nodes.
"[document.documentElement, -1]",
"[document.documentElement, 0]",
"[document.documentElement, 1]",
"[document.documentElement, 2]",
"[document.documentElement, 7]",
"[document.head, 1]",
"[document.body, 3]",
"[foreignDoc.documentElement, 0]",
"[foreignDoc.documentElement, 1]",
"[foreignDoc.head, 0]",
"[foreignDoc.body, 1]",
"[paras[0], 0]",
"[paras[0], 1]",
"[paras[0], 2]",
"[paras[1], 0]",
"[paras[1], 1]",
"[paras[1], 2]",
"[detachedPara1, 0]",
"[detachedPara1, 1]",
"[testDiv, 0]",
"[testDiv, 3]",
// Then a few more interesting things just for good measure.
"[document, -1]",
"[document, 0]",
"[document, 1]",
"[document, 2]",
"[document, 3]",
"[comment, -1]",
"[comment, 0]",
"[comment, 4]",
"[comment, 96]",
"[foreignDoc, 0]",
"[foreignDoc, 1]",
"[foreignComment, 2]",
"[foreignTextNode, 0]",
"[foreignTextNode, 36]",
"[xmlDoc, -1]",
"[xmlDoc, 0]",
"[xmlDoc, 1]",
"[xmlDoc, 5]",
"[xmlComment, 0]",
"[xmlComment, 4]",
"[processingInstruction, 0]",
"[processingInstruction, 5]",
"[processingInstruction, 9]",
"[detachedTextNode, 0]",
"[detachedTextNode, 8]",
"[detachedForeignTextNode, 0]",
"[detachedForeignTextNode, 8]",
"[detachedXmlTextNode, 0]",
"[detachedXmlTextNode, 8]",
"[detachedProcessingInstruction, 12]",
"[detachedComment, 3]",
"[detachedComment, 5]",
"[detachedForeignComment, 0]",
"[detachedForeignComment, 4]",
"[detachedXmlComment, 2]",
"[docfrag, 0]",
"[foreignDocfrag, 0]",
"[xmlDocfrag, 0]",
"[doctype, 0]",
"[doctype, -17]",
"[doctype, 1]",
"[foreignDoctype, 0]",
"[xmlDoctype, 0]",
];
testNodes = [
"paras[0]",
"paras[0].firstChild",
"paras[1]",
"paras[1].firstChild",
"foreignPara1",
"foreignPara1.firstChild",
"detachedPara1",
"detachedPara1.firstChild",
"detachedPara1",
"detachedPara1.firstChild",
"testDiv",
"document",
"detachedDiv",
"detachedPara2",
"foreignDoc",
"foreignPara2",
"xmlDoc",
"xmlElement",
"detachedXmlElement",
"detachedTextNode",
"foreignTextNode",
"detachedForeignTextNode",
"xmlTextNode",
"detachedXmlTextNode",
"processingInstruction",
"detachedProcessingInstruction",
"comment",
"detachedComment",
"foreignComment",
"detachedForeignComment",
"xmlComment",
"detachedXmlComment",
"docfrag",
"foreignDocfrag",
"xmlDocfrag",
"doctype",
"foreignDoctype",
"xmlDoctype",
];
}
if ("setup" in window) {
setup(setupRangeTests);
} else {
// Presumably we're running from within an iframe or something
setupRangeTests();
}
/**
* Return the length of a node as specified in DOM Range.
*/
function getNodeLength(node) {
if (node.nodeType == Node.DOCUMENT_TYPE_NODE) {
return 0;
}
if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.PROCESSING_INSTRUCTION_NODE || node.nodeType == Node.COMMENT_NODE) {
return node.length;
}
return node.childNodes.length;
}
/**
* Returns the furthest ancestor of a Node as defined by the spec.
*/
function furthestAncestor(node) {
var root = node;
while (root.parentNode != null) {
root = root.parentNode;
}
return root;
}
/**
* "The ancestor containers of a Node are the Node itself and all its
* ancestors."
*
* Is node1 an ancestor container of node2?
*/
function isAncestorContainer(node1, node2) {
return node1 == node2 ||
(node2.compareDocumentPosition(node1) & Node.DOCUMENT_POSITION_CONTAINS);
}
/**
* Returns the first Node that's after node in tree order, or null if node is
* the last Node.
*/
function nextNode(node) {
if (node.hasChildNodes()) {
return node.firstChild;
}
return nextNodeDescendants(node);
}
/**
* Returns the last Node that's before node in tree order, or null if node is
* the first Node.
*/
function previousNode(node) {
if (node.previousSibling) {
node = node.previousSibling;
while (node.hasChildNodes()) {
node = node.lastChild;
}
return node;
}
return node.parentNode;
}
/**
* Returns the next Node that's after node and all its descendants in tree
* order, or null if node is the last Node or an ancestor of it.
*/
function nextNodeDescendants(node) {
while (node && !node.nextSibling) {
node = node.parentNode;
}
if (!node) {
return null;
}
return node.nextSibling;
}
/**
* Returns the ownerDocument of the Node, or the Node itself if it's a
* Document.
*/
function ownerDocument(node) {
return node.nodeType == Node.DOCUMENT_NODE
? node
: node.ownerDocument;
}
/**
* Returns true if ancestor is an ancestor of descendant, false otherwise.
*/
function isAncestor(ancestor, descendant) {
if (!ancestor || !descendant) {
return false;
}
while (descendant && descendant != ancestor) {
descendant = descendant.parentNode;
}
return descendant == ancestor;
}
/**
* Returns true if descendant is a descendant of ancestor, false otherwise.
*/
function isDescendant(descendant, ancestor) {
return isAncestor(ancestor, descendant);
}
/**
* The position of two boundary points relative to one another, as defined by
* the spec.
*/
function getPosition(nodeA, offsetA, nodeB, offsetB) {
// "If node A is the same as node B, return equal if offset A equals offset
// B, before if offset A is less than offset B, and after if offset A is
// greater than offset B."
if (nodeA == nodeB) {
if (offsetA == offsetB) {
return "equal";
}
if (offsetA < offsetB) {
return "before";
}
if (offsetA > offsetB) {
return "after";
}
}
// "If node A is after node B in tree order, compute the position of (node
// B, offset B) relative to (node A, offset A). If it is before, return
// after. If it is after, return before."
if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_FOLLOWING) {
var pos = getPosition(nodeB, offsetB, nodeA, offsetA);
if (pos == "before") {
return "after";
}
if (pos == "after") {
return "before";
}
}
// "If node A is an ancestor of node B:"
if (nodeB.compareDocumentPosition(nodeA) & Node.DOCUMENT_POSITION_CONTAINS) {
// "Let child equal node B."
var child = nodeB;
// "While child is not a child of node A, set child to its parent."
while (child.parentNode != nodeA) {
child = child.parentNode;
}
// "If the index of child is less than offset A, return after."
if (indexOf(child) < offsetA) {
return "after";
}
}
// "Return before."
return "before";
}
/**
* "contained" as defined by DOM Range: "A Node node is contained in a range
* range if node's furthest ancestor is the same as range's root, and (node, 0)
* is after range's start, and (node, length of node) is before range's end."
*/
function isContained(node, range) {
var pos1 = getPosition(node, 0, range.startContainer, range.startOffset);
var pos2 = getPosition(node, getNodeLength(node), range.endContainer, range.endOffset);
return furthestAncestor(node) == furthestAncestor(range.startContainer)
&& pos1 == "after"
&& pos2 == "before";
}
/**
* "partially contained" as defined by DOM Range: "A Node is partially
* contained in a range if it is an ancestor container of the range's start but
* not its end, or vice versa."
*/
function isPartiallyContained(node, range) {
var cond1 = isAncestorContainer(node, range.startContainer);
var cond2 = isAncestorContainer(node, range.endContainer);
return (cond1 && !cond2) || (cond2 && !cond1);
}
/**
* Index of a node as defined by the spec.
*/
function indexOf(node) {
if (!node.parentNode) {
// No preceding sibling nodes, right?
return 0;
}
var i = 0;
while (node != node.parentNode.childNodes[i]) {
i++;
}
return i;
}
/**
* extractContents() implementation, following the spec. If an exception is
* supposed to be thrown, will return a string with the name (e.g.,
* "HIERARCHY_REQUEST_ERR") instead of a document fragment. It might also
* return an arbitrary human-readable string if a condition is hit that implies
* a spec bug.
*/
function myExtractContents(range) {
// "If the context object's detached flag is set, raise an
// INVALID_STATE_ERR exception and abort these steps."
try {
range.collapsed;
} catch (e) {
return "INVALID_STATE_ERR";
}
// "Let frag be a new DocumentFragment whose ownerDocument is the same as
// the ownerDocument of the context object's start node."
var ownerDoc = range.startContainer.nodeType == Node.DOCUMENT_NODE
? range.startContainer
: range.startContainer.ownerDocument;
var frag = ownerDoc.createDocumentFragment();
// "If the context object's start and end are the same, abort this method,
// returning frag."
if (range.startContainer == range.endContainer
&& range.startOffset == range.endOffset) {
return frag;
}
// "Let original start node, original start offset, original end node, and
// original end offset be the context object's start and end nodes and
// offsets, respectively."
var originalStartNode = range.startContainer;
var originalStartOffset = range.startOffset;
var originalEndNode = range.endContainer;
var originalEndOffset = range.endOffset;
// "If original start node and original end node are the same, and they are
// a Text or Comment node:"
if (range.startContainer == range.endContainer
&& (range.startContainer.nodeType == Node.TEXT_NODE
|| range.startContainer.nodeType == Node.COMMENT_NODE)) {
// "Let clone be the result of calling cloneNode(false) on original
// start node."
var clone = originalStartNode.cloneNode(false);
// "Set the data of clone to the result of calling
// substringData(original start offset, original end offset original
// start offset) on original start node."
clone.data = originalStartNode.substringData(originalStartOffset,
originalEndOffset - originalStartOffset);
// "Append clone as the last child of frag."
frag.appendChild(clone);
// "Call deleteData(original start offset, original end offset
// original start offset) on original start node."
originalStartNode.deleteData(originalStartOffset,
originalEndOffset - originalStartOffset);
// "Abort this method, returning frag."
return frag;
}
// "Let common ancestor equal original start node."
var commonAncestor = originalStartNode;
// "While common ancestor is not an ancestor container of original end
// node, set common ancestor to its own parent."
while (!isAncestorContainer(commonAncestor, originalEndNode)) {
commonAncestor = commonAncestor.parentNode;
}
// "If original start node is an ancestor container of original end node,
// let first partially contained child be null."
var firstPartiallyContainedChild;
if (isAncestorContainer(originalStartNode, originalEndNode)) {
firstPartiallyContainedChild = null;
// "Otherwise, let first partially contained child be the first child of
// common ancestor that is partially contained in the context object."
} else {
for (var i = 0; i < commonAncestor.childNodes.length; i++) {
if (isPartiallyContained(commonAncestor.childNodes[i], range)) {
firstPartiallyContainedChild = commonAncestor.childNodes[i];
break;
}
}
if (!firstPartiallyContainedChild) {
throw "Spec bug: no first partially contained child!";
}
}
// "If original end node is an ancestor container of original start node,
// let last partially contained child be null."
var lastPartiallyContainedChild;
if (isAncestorContainer(originalEndNode, originalStartNode)) {
lastPartiallyContainedChild = null;
// "Otherwise, let last partially contained child be the last child of
// common ancestor that is partially contained in the context object."
} else {
for (var i = commonAncestor.childNodes.length - 1; i >= 0; i--) {
if (isPartiallyContained(commonAncestor.childNodes[i], range)) {
lastPartiallyContainedChild = commonAncestor.childNodes[i];
break;
}
}
if (!lastPartiallyContainedChild) {
throw "Spec bug: no last partially contained child!";
}
}
// "Let contained children be a list of all children of common ancestor
// that are contained in the context object, in tree order."
//
// "If any member of contained children is a DocumentType, raise a
// HIERARCHY_REQUEST_ERR exception and abort these steps."
var containedChildren = [];
for (var i = 0; i < commonAncestor.childNodes.length; i++) {
if (isContained(commonAncestor.childNodes[i], range)) {
if (commonAncestor.childNodes[i].nodeType
== Node.DOCUMENT_TYPE_NODE) {
return "HIERARCHY_REQUEST_ERR";
}
containedChildren.push(commonAncestor.childNodes[i]);
}
}
// "If original start node is an ancestor container of original end node,
// set new node to original start node and new offset to original start
// offset."
var newNode, newOffset;
if (isAncestorContainer(originalStartNode, originalEndNode)) {
newNode = originalStartNode;
newOffset = originalStartOffset;
// "Otherwise:"
} else {
// "Let reference node equal original start node."
var referenceNode = originalStartNode;
// "While reference node's parent is not null and is not an ancestor
// container of original end node, set reference node to its parent."
while (referenceNode.parentNode
&& !isAncestorContainer(referenceNode.parentNode, originalEndNode)) {
referenceNode = referenceNode.parentNode;
}
// "Set new node to the parent of reference node, and new offset to one
// plus the index of reference node."
newNode = referenceNode.parentNode;
newOffset = 1 + indexOf(referenceNode);
}
// "If first partially contained child is a Text or Comment node:"
if (firstPartiallyContainedChild
&& (firstPartiallyContainedChild.nodeType == Node.TEXT_NODE
|| firstPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) {
// "Let clone be the result of calling cloneNode(false) on original
// start node."
var clone = originalStartNode.cloneNode(false);
// "Set the data of clone to the result of calling substringData() on
// original start node, with original start offset as the first
// argument and (length of original start node original start offset)
// as the second."
clone.data = originalStartNode.substringData(originalStartOffset,
getNodeLength(originalStartNode) - originalStartOffset);
// "Append clone as the last child of frag."
frag.appendChild(clone);
// "Call deleteData() on original start node, with original start
// offset as the first argument and (length of original start node
// original start offset) as the second."
originalStartNode.deleteData(originalStartOffset,
getNodeLength(originalStartNode) - originalStartOffset);
// "Otherwise, if first partially contained child is not null:"
} else if (firstPartiallyContainedChild) {
// "Let clone be the result of calling cloneNode(false) on first
// partially contained child."
var clone = firstPartiallyContainedChild.cloneNode(false);
// "Append clone as the last child of frag."
frag.appendChild(clone);
// "Let subrange be a new Range whose start is (original start node,
// original start offset) and whose end is (first partially contained
// child, length of first partially contained child)."
var subrange = ownerDoc.createRange();
subrange.setStart(originalStartNode, originalStartOffset);
subrange.setEnd(firstPartiallyContainedChild,
getNodeLength(firstPartiallyContainedChild));
// "Let subfrag be the result of calling extractContents() on
// subrange."
var subfrag = myExtractContents(subrange);
// "For each child of subfrag, in order, append that child to clone as
// its last child."
for (var i = 0; i < subfrag.childNodes.length; i++) {
clone.appendChild(subfrag.childNodes[i]);
}
}
// "For each contained child in contained children, append contained child
// as the last child of frag."
for (var i = 0; i < containedChildren.length; i++) {
frag.appendChild(containedChildren[i]);
}
// "If last partially contained child is a Text or Comment node:"
if (lastPartiallyContainedChild
&& (lastPartiallyContainedChild.nodeType == Node.TEXT_NODE
|| lastPartiallyContainedChild.nodeType == Node.COMMENT_NODE)) {
// "Let clone be the result of calling cloneNode(false) on original
// end node."
var clone = originalEndNode.cloneNode(false);
// "Set the data of clone to the result of calling substringData(0,
// original end offset) on original end node."
clone.data = originalEndNode.substringData(0, originalEndOffset);
// "Append clone as the last child of frag."
frag.appendChild(clone);
// "Call deleteData(0, original end offset) on original end node."
originalEndNode.deleteData(0, originalEndOffset);
// "Otherwise, if last partially contained child is not null:"
} else if (lastPartiallyContainedChild) {
// "Let clone be the result of calling cloneNode(false) on last
// partially contained child."
var clone = lastPartiallyContainedChild.cloneNode(false);
// "Append clone as the last child of frag."
frag.appendChild(clone);
// "Let subrange be a new Range whose start is (last partially
// contained child, 0) and whose end is (original end node, original
// end offset)."
var subrange = ownerDoc.createRange();
subrange.setStart(lastPartiallyContainedChild, 0);
subrange.setEnd(originalEndNode, originalEndOffset);
// "Let subfrag be the result of calling extractContents() on
// subrange."
var subfrag = myExtractContents(subrange);
// "For each child of subfrag, in order, append that child to clone as
// its last child."
for (var i = 0; i < subfrag.childNodes.length; i++) {
clone.appendChild(subfrag.childNodes[i]);
}
}
// "Set the context object's start and end to (new node, new offset)."
range.setStart(newNode, newOffset);
range.setEnd(newNode, newOffset);
// "Return frag."
return frag;
}
/**
* insertNode() implementation, following the spec. If an exception is
* supposed to be thrown, will return a string with the name (e.g.,
* "HIERARCHY_REQUEST_ERR") instead of a document fragment. It might also
* return an arbitrary human-readable string if a condition is hit that implies
* a spec bug.
*/
function myInsertNode(range, newNode) {
// "If the context object's detached flag is set, raise an
// INVALID_STATE_ERR exception and abort these steps."
//
// Assume that if accessing collapsed throws, it's detached.
try {
range.collapsed;
} catch (e) {
return "INVALID_STATE_ERR";
}
// "If the context object's start node is a Text or Comment node and its
// parent is null, raise an HIERARCHY_REQUEST_ERR exception and abort these
// steps."
if ((range.startContainer.nodeType == Node.TEXT_NODE
|| range.startContainer.nodeType == Node.COMMENT_NODE)
&& !range.startContainer.parentNode) {
return "HIERARCHY_REQUEST_ERR";
}
// "If the context object's start node is a Text node, run splitText() on
// it with the context object's start offset as its argument, and let
// reference node be the result."
var referenceNode;
if (range.startContainer.nodeType == Node.TEXT_NODE) {
// We aren't testing how ranges vary under mutations, and browsers vary
// in how they mutate for splitText, so let's just force the correct
// way.
var start = [range.startContainer, range.startOffset];
var end = [range.endContainer, range.endOffset];
referenceNode = range.startContainer.splitText(range.startOffset);
if (start[0] == end[0]
&& end[1] > start[1]) {
end[0] = referenceNode;
end[1] -= start[1];
} else if (end[0] == start[0].parentNode
&& end[1] > indexOf(referenceNode)) {
end[1]++;
}
range.setStart(start[0], start[1]);
range.setEnd(end[0], end[1]);
// "Otherwise, if the context object's start node is a Comment, let
// reference node be the context object's start node."
} else if (range.startContainer.nodeType == Node.COMMENT_NODE) {
referenceNode = range.startContainer;
// "Otherwise, let reference node be the child of the context object's
// start node with index equal to the context object's start offset, or
// null if there is no such child."
} else {
referenceNode = range.startContainer.childNodes[range.startOffset];
if (typeof referenceNode == "undefined") {
referenceNode = null;
}
}
// "If reference node is null, let parent node be the context object's
// start node."
var parentNode;
if (!referenceNode) {
parentNode = range.startContainer;
// "Otherwise, let parent node be the parent of reference node."
} else {
parentNode = referenceNode.parentNode;
}
// "Call insertBefore(newNode, reference node) on parent node, re-raising
// any exceptions that call raised."
try {
parentNode.insertBefore(newNode, referenceNode);
} catch (e) {
return getDomExceptionName(e);
}
}
/**
* Asserts that two nodes are equal, in the sense of isEqualNode(). If they
* aren't, tries to print a relatively informative reason why not. TODO: Move
* this to testharness.js?
*/
function assertNodesEqual(actual, expected, msg) {
if (!actual.isEqualNode(expected)) {
msg = "Actual and expected mismatch for " + msg + ". ";
while (actual && expected) {
assert_true(actual.nodeType === expected.nodeType
&& actual.nodeName === expected.nodeName
&& actual.nodeValue === expected.nodeValue
&& actual.childNodes.length === expected.childNodes.length,
"First differing node: expected " + format_value(expected)
+ ", got " + format_value(actual));
actual = nextNode(actual);
expected = nextNode(expected);
}
assert_unreached("DOMs were not equal but we couldn't figure out why");
}
}
/**
* Given a DOMException, return the name (e.g., "HIERARCHY_REQUEST_ERR"). In
* theory this should be just e.name, but in practice it's not. So I could
* legitimately just return e.name, but then every engine but WebKit would fail
* every test, since no one seems to care much for standardizing DOMExceptions.
* Instead I mangle it to account for browser bugs, so as not to fail
* insertNode() tests (for instance) for insertBefore() bugs. Of course, a
* standards-compliant browser will work right in any event.
*
* If the exception has no string property called "name" or "message", we just
* re-throw it.
*/
function getDomExceptionName(e) {
if (typeof e.name == "string"
&& /^[A-Z_]+_ERR$/.test(e.name)) {
// Either following the standard, or prefixing NS_ERROR_DOM (I'm
// looking at you, Gecko).
return e.name.replace(/^NS_ERROR_DOM_/, "");
}
if (typeof e.message == "string"
&& /^[A-Z_]+_ERR$/.test(e.message)) {
// Opera
return e.message;
}
if (typeof e.message == "string"
&& /^DOM Exception:/.test(e.message)) {
// IE
return /[A-Z_]+_ERR/.exec(e.message)[0];
}
throw e;
}
/**
* Given an array of endpoint data [start container, start offset, end
* container, end offset], returns a Range with those endpoints.
*/
function rangeFromEndpoints(endpoints) {
// If we just use document instead of the ownerDocument of endpoints[0],
// WebKit will throw on setStart/setEnd. This is a WebKit bug, but it's in
// range, not selection, so we don't want to fail anything for it.
var range = ownerDocument(endpoints[0]).createRange();
range.setStart(endpoints[0], endpoints[1]);
range.setEnd(endpoints[2], endpoints[3]);
return range;
}
/**
* Given an array of endpoint data [start container, start offset, end
* container, end offset], sets the selection to have those endpoints. Uses
* addRange, so the range will be forwards. Accepts an empty array for
* endpoints, in which case the selection will just be emptied.
*/
function setSelectionForwards(endpoints) {
selection.removeAllRanges();
if (endpoints.length) {
selection.addRange(rangeFromEndpoints(endpoints));
}
}
/**
* Given an array of endpoint data [start container, start offset, end
* container, end offset], sets the selection to have those endpoints, with the
* direction backwards. Uses extend, so it will throw in IE. Accepts an empty
* array for endpoints, in which case the selection will just be emptied.
*/
function setSelectionBackwards(endpoints) {
selection.removeAllRanges();
if (endpoints.length) {
selection.collapse(endpoints[2], endpoints[3]);
selection.extend(endpoints[0], endpoints[1]);
}
}

View File

@ -1,33 +0,0 @@
<!doctype html>
<title>Selection test iframe</title>
<link rel=author title="Aryeh Gregor" href=ayg@aryeh.name>
<body>
<script src=common.js></script>
<script>
"use strict";
// This script only exists because we want to evaluate the range endpoints
// in each iframe using that iframe's local variables set up by common.js. It
// just creates a range with the endpoints given by
// eval(window.testRangeInput), and assigns the result to window.testRange. If
// there's an exception, it's assigned to window.unexpectedException.
// Everything else is to be done by the script that created the iframe.
window.unexpectedException = null;
function run() {
window.unexpectedException = null;
try {
window.testRange = rangeFromEndpoints(eval(window.testRangeInput));
} catch(e) {
window.unexpectedException = e;
}
}
// Remove the scripts so they don't run repeatedly when the iframe is
// reinitialized
[].forEach.call(document.querySelectorAll("script"), function(script) {
script.parentNode.removeChild(script);
});
testDiv.style.display = "none";
</script>

View File

@ -1,44 +0,0 @@
<!doctype html>
<title>Selection Document.open() tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
"use strict";
// This tests the HTML spec requirement "Replace the Document's singleton
// objects with new instances of those objects. (This includes in particular
// the Window, Location, History, ApplicationCache, and Navigator, objects, the
// various BarProp objects, the two Storage objects, the various HTMLCollection
// objects, and objects defined by other specifications, like Selection and the
// document's UndoManager. It also includes all the Web IDL prototypes in the
// JavaScript binding, including the Document object's prototype.)" in the
// document.open() algorithm.
var iframe = document.createElement("iframe");
var t = async_test("Selection must be replaced with a new object after document.open()");
iframe.onload = function() {
t.step(function() {
var originalSelection = iframe.contentWindow.getSelection();
assert_equals(originalSelection.rangeCount, 0,
"Sanity check: rangeCount must be initially 0");
iframe.contentDocument.body.appendChild(
iframe.contentDocument.createTextNode("foo"));
var range = iframe.contentDocument.createRange();
range.selectNodeContents(iframe.contentDocument.body);
iframe.contentWindow.getSelection().addRange(range);
assert_equals(originalSelection.rangeCount, 1,
"Sanity check: rangeCount must be 1 after adding a range");
iframe.contentDocument.open();
assert_not_equals(iframe.contentWindow.getSelection(), originalSelection,
"After document.open(), the Selection object must no longer be the same");
assert_equals(iframe.contentWindow.getSelection().rangeCount, 0,
"After document.open(), rangeCount must be 0 again");
});
t.done();
document.body.removeChild(iframe);
};
document.body.appendChild(iframe);
</script>

View File

@ -1,177 +0,0 @@
<!doctype html>
<title>Selection.addRange() tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
function testAddRange(exception, range, endpoints, qualifier, testName) {
test(function() {
assert_equals(exception, null, "Test setup must not throw exceptions");
selection.addRange(range);
assert_equals(range.startContainer, endpoints[0],
"addRange() must not modify the startContainer of the Range it's given");
assert_equals(range.startOffset, endpoints[1],
"addRange() must not modify the startOffset of the Range it's given");
assert_equals(range.endContainer, endpoints[2],
"addRange() must not modify the endContainer of the Range it's given");
assert_equals(range.endOffset, endpoints[3],
"addRange() must not modify the endOffset of the Range it's given");
}, testName + ": " + qualifier + " addRange() must not throw exceptions or modify the range it's given");
test(function() {
assert_equals(exception, null, "Test setup must not throw exceptions");
assert_equals(selection.rangeCount, 1, "rangeCount must be 1");
}, testName + ": " + qualifier + " addRange() must result in rangeCount being 1");
// From here on out we check selection.getRangeAt(selection.rangeCount - 1)
// so as not to double-fail Gecko.
test(function() {
assert_equals(exception, null, "Test setup must not throw exceptions");
assert_not_equals(selection.rangeCount, 0, "Cannot proceed with tests if rangeCount is 0");
var newRange = selection.getRangeAt(selection.rangeCount - 1);
assert_not_equals(newRange, null,
"getRangeAt(rangeCount - 1) must not return null");
assert_equals(typeof newRange, "object",
"getRangeAt(rangeCount - 1) must return an object");
assert_equals(newRange.startContainer, range.startContainer,
"startContainer of the Selection's last Range must match the added Range");
assert_equals(newRange.startOffset, range.startOffset,
"startOffset of the Selection's last Range must match the added Range");
assert_equals(newRange.endContainer, range.endContainer,
"endContainer of the Selection's last Range must match the added Range");
assert_equals(newRange.endOffset, range.endOffset,
"endOffset of the Selection's last Range must match the added Range");
}, testName + ": " + qualifier + " addRange() must result in the selection's last range having the specified endpoints");
test(function() {
assert_equals(exception, null, "Test setup must not throw exceptions");
assert_not_equals(selection.rangeCount, 0, "Cannot proceed with tests if rangeCount is 0");
assert_equals(selection.getRangeAt(selection.rangeCount - 1), range,
"getRangeAt(rangeCount - 1) must return the same object we added");
}, testName + ": " + qualifier + " addRange() must result in the selection's last range being the same object we added");
// Let's not test many different modifications -- one should be enough.
test(function() {
assert_equals(exception, null, "Test setup must not throw exceptions");
assert_not_equals(selection.rangeCount, 0, "Cannot proceed with tests if rangeCount is 0");
if (range.startContainer == paras[0].firstChild
&& range.startOffset == 0
&& range.endContainer == paras[0].firstChild
&& range.endOffset == 2) {
// Just in case . . .
range.setStart(paras[0].firstChild, 1);
} else {
range.setStart(paras[0].firstChild, 0);
range.setEnd(paras[0].firstChild, 2);
}
var newRange = selection.getRangeAt(selection.rangeCount - 1);
assert_equals(newRange.startContainer, range.startContainer,
"After mutating the " + qualifier + " added Range, startContainer of the Selection's last Range must match the added Range");
assert_equals(newRange.startOffset, range.startOffset,
"After mutating the " + qualifier + " added Range, startOffset of the Selection's last Range must match the added Range");
assert_equals(newRange.endContainer, range.endContainer,
"After mutating the " + qualifier + " added Range, endContainer of the Selection's last Range must match the added Range");
assert_equals(newRange.endOffset, range.endOffset,
"After mutating the " + qualifier + " added Range, endOffset of the Selection's last Range must match the added Range");
}, testName + ": modifying the " + qualifier + " added range must modify the Selection's last Range");
// Now test the other way too.
test(function() {
assert_equals(exception, null, "Test setup must not throw exceptions");
assert_not_equals(selection.rangeCount, 0, "Cannot proceed with tests if rangeCount is 0");
var newRange = selection.getRangeAt(selection.rangeCount - 1);
if (newRange.startContainer == paras[0].firstChild
&& newRange.startOffset == 4
&& newRange.endContainer == paras[0].firstChild
&& newRange.endOffset == 6) {
newRange.setStart(paras[0].firstChild, 5);
} else {
newRange.setStart(paras[0].firstChild, 4);
newRange.setStart(paras[0].firstChild, 6);
}
assert_equals(newRange.startContainer, range.startContainer,
"After " + qualifier + " addRange(), after mutating the Selection's last Range, startContainer of the Selection's last Range must match the added Range");
assert_equals(newRange.startOffset, range.startOffset,
"After " + qualifier + " addRange(), after mutating the Selection's last Range, startOffset of the Selection's last Range must match the added Range");
assert_equals(newRange.endContainer, range.endContainer,
"After " + qualifier + " addRange(), after mutating the Selection's last Range, endContainer of the Selection's last Range must match the added Range");
assert_equals(newRange.endOffset, range.endOffset,
"After " + qualifier + " addRange(), after mutating the Selection's last Range, endOffset of the Selection's last Range must match the added Range");
}, testName + ": modifying the Selection's last Range must modify the " + qualifier + " added Range");
}
// Do only n evals, not n^2
var testRangesEvaled = testRanges.map(eval);
for (var i = 0; i < testRanges.length; i++) {
for (var j = 0; j < testRanges.length; j++) {
var testName = "Range " + i + " " + testRanges[i]
+ " followed by Range " + j + " " + testRanges[j];
var exception = null;
try {
selection.removeAllRanges();
var endpoints1 = testRangesEvaled[i];
var range1 = ownerDocument(endpoints1[0]).createRange();
range1.setStart(endpoints1[0], endpoints1[1]);
range1.setEnd(endpoints1[2], endpoints1[3]);
if (range1.startContainer !== endpoints1[0]) {
throw "Sanity check: the first Range we created must have the desired startContainer";
}
if (range1.startOffset !== endpoints1[1]) {
throw "Sanity check: the first Range we created must have the desired startOffset";
}
if (range1.endContainer !== endpoints1[2]) {
throw "Sanity check: the first Range we created must have the desired endContainer";
}
if (range1.endOffset !== endpoints1[3]) {
throw "Sanity check: the first Range we created must have the desired endOffset";
}
var endpoints2 = testRangesEvaled[j];
var range2 = ownerDocument(endpoints2[0]).createRange();
range2.setStart(endpoints2[0], endpoints2[1]);
range2.setEnd(endpoints2[2], endpoints2[3]);
if (range2.startContainer !== endpoints2[0]) {
throw "Sanity check: the second Range we created must have the desired startContainer";
}
if (range2.startOffset !== endpoints2[1]) {
throw "Sanity check: the second Range we created must have the desired startOffset";
}
if (range2.endContainer !== endpoints2[2]) {
throw "Sanity check: the second Range we created must have the desired endContainer";
}
if (range2.endOffset !== endpoints2[3]) {
throw "Sanity check: the second Range we created must have the desired endOffset";
}
} catch (e) {
exception = e;
}
testAddRange(exception, range1, endpoints1, "first", testName);
testAddRange(exception, range2, endpoints2, "second", testName);
}
}
testDiv.style.display = "none";
</script>

View File

@ -1,91 +0,0 @@
<!doctype html>
<title>Selection.collapse() tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
if (W3CTest.runner) {
W3CTest.runner.requestLongerTimeout(5);
}
function testCollapse(range, point) {
selection.removeAllRanges();
var addedRange;
if (range) {
addedRange = range.cloneRange();
selection.addRange(addedRange);
}
if (point[0].nodeType == Node.DOCUMENT_TYPE_NODE) {
assert_throws("INVALID_NODE_TYPE_ERR", function() {
selection.collapse(point[0], point[1]);
}, "Must throw INVALID_NODE_TYPE_ERR when collapse()ing if the node is a DocumentType");
return;
}
if (point[1] < 0 || point[1] > getNodeLength(point[0])) {
assert_throws("INDEX_SIZE_ERR", function() {
selection.collapse(point[0], point[1]);
}, "Must throw INDEX_SIZE_ERR when collapse()ing if the offset is negative or greater than the node's length");
return;
}
selection.collapse(point[0], point[1]);
assert_equals(selection.rangeCount, 1,
"selection.rangeCount must equal 1 after collapse()");
assert_equals(selection.focusNode, point[0],
"focusNode must equal the node we collapse()d to");
assert_equals(selection.focusOffset, point[1],
"focusOffset must equal the offset we collapse()d to");
assert_equals(selection.focusNode, selection.anchorNode,
"focusNode and anchorNode must be equal after collapse()");
assert_equals(selection.focusOffset, selection.anchorOffset,
"focusOffset and anchorOffset must be equal after collapse()");
if (range) {
assert_equals(addedRange.startContainer, range.startContainer,
"collapse() must not change the startContainer of a preexisting Range");
assert_equals(addedRange.endContainer, range.endContainer,
"collapse() must not change the endContainer of a preexisting Range");
assert_equals(addedRange.startOffset, range.startOffset,
"collapse() must not change the startOffset of a preexisting Range");
assert_equals(addedRange.endOffset, range.endOffset,
"collapse() must not change the endOffset of a preexisting Range");
}
}
// Also test a selection with no ranges
testRanges.unshift("[]");
// Don't want to eval() each point a bazillion times
var testPointsCached = [];
for (var i = 0; i < testPoints.length; i++) {
testPointsCached.push(eval(testPoints[i]));
}
var tests = [];
for (var i = 0; i < testRanges.length; i++) {
var endpoints = eval(testRanges[i]);
var range;
test(function() {
if (endpoints.length) {
range = ownerDocument(endpoints[0]).createRange();
range.setStart(endpoints[0], endpoints[1]);
range.setEnd(endpoints[2], endpoints[3]);
} else {
// Empty selection
range = null;
}
}, "Set up range " + i + " " + testRanges[i]);
for (var j = 0; j < testPoints.length; j++) {
tests.push(["Range " + i + " " + testRanges[i] + ", point " + j + " " + testPoints[j], range, testPointsCached[j]]);
}
}
generate_tests(testCollapse, tests);
testDiv.style.display = "none";
</script>

View File

@ -1,121 +0,0 @@
<!doctype html>
<title>Selection.collapseTo(Start|End)() tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
// Also test a selection with no ranges
testRanges.unshift("[]");
for (var i = 0; i < testRanges.length; i++) {
test(function() {
selection.removeAllRanges();
var endpoints = eval(testRanges[i]);
if (!endpoints.length) {
assert_throws("INVALID_STATE_ERR", function() {
selection.collapseToStart();
}, "Must throw InvalidStateErr if the selection's range is null");
return;
}
var addedRange = ownerDocument(endpoints[0]).createRange();
addedRange.setStart(endpoints[0], endpoints[1]);
addedRange.setEnd(endpoints[2], endpoints[3]);
selection.addRange(addedRange);
// We don't penalize browsers here for mishandling addRange() and
// adding a different range than we specified. They fail addRange()
// tests for that, and don't have to fail collapseToStart/End() tests
// too. They do fail if they throw unexpectedly, though. I also fail
// them if there's no range at all, because otherwise they could pass
// all tests if addRange() always does nothing and collapseToStart()
// always throws.
assert_equals(selection.rangeCount, 1,
"Sanity check: rangeCount must equal 1 after addRange()");
var expectedEndpoint = [
selection.getRangeAt(0).startContainer,
selection.getRangeAt(0).startOffset
];
selection.collapseToStart();
assert_equals(selection.rangeCount, 1,
"selection.rangeCount must equal 1");
assert_equals(selection.focusNode, expectedEndpoint[0],
"focusNode must equal the original start node");
assert_equals(selection.focusOffset, expectedEndpoint[1],
"focusOffset must equal the original start offset");
assert_equals(selection.anchorNode, expectedEndpoint[0],
"anchorNode must equal the original start node");
assert_equals(selection.anchorOffset, expectedEndpoint[1],
"anchorOffset must equal the original start offset");
assert_equals(addedRange.startContainer, endpoints[0],
"collapseToStart() must not change the startContainer of the selection's original range");
assert_equals(addedRange.startOffset, endpoints[1],
"collapseToStart() must not change the startOffset of the selection's original range");
assert_equals(addedRange.endContainer, endpoints[2],
"collapseToStart() must not change the endContainer of the selection's original range");
assert_equals(addedRange.endOffset, endpoints[3],
"collapseToStart() must not change the endOffset of the selection's original range");
}, "Range " + i + " " + testRanges[i] + " collapseToStart()");
// Copy-paste of above
test(function() {
selection.removeAllRanges();
var endpoints = eval(testRanges[i]);
if (!endpoints.length) {
assert_throws("INVALID_STATE_ERR", function() {
selection.collapseToEnd();
}, "Must throw InvalidStateErr if the selection's range is null");
return;
}
var addedRange = ownerDocument(endpoints[0]).createRange();
addedRange.setStart(endpoints[0], endpoints[1]);
addedRange.setEnd(endpoints[2], endpoints[3]);
selection.addRange(addedRange);
// We don't penalize browsers here for mishandling addRange() and
// adding a different range than we specified. They fail addRange()
// tests for that, and don't have to fail collapseToStart/End() tests
// too. They do fail if they throw unexpectedly, though. I also fail
// them if there's no range at all, because otherwise they could pass
// all tests if addRange() always does nothing and collapseToStart()
// always throws.
assert_equals(selection.rangeCount, 1,
"Sanity check: rangeCount must equal 1 after addRange()");
var expectedEndpoint = [
selection.getRangeAt(0).endContainer,
selection.getRangeAt(0).endOffset
];
selection.collapseToEnd();
assert_equals(selection.rangeCount, 1,
"selection.rangeCount must equal 1");
assert_equals(selection.focusNode, expectedEndpoint[0],
"focusNode must equal the original end node");
assert_equals(selection.focusOffset, expectedEndpoint[1],
"focusOffset must equal the original end offset");
assert_equals(selection.anchorNode, expectedEndpoint[0],
"anchorNode must equal the original end node");
assert_equals(selection.anchorOffset, expectedEndpoint[1],
"anchorOffset must equal the original end offset");
assert_equals(addedRange.startContainer, endpoints[0],
"collapseToEnd() must not change the startContainer of the selection's original range");
assert_equals(addedRange.startOffset, endpoints[1],
"collapseToEnd() must not change the startOffset of the selection's original range");
assert_equals(addedRange.endContainer, endpoints[2],
"collapseToEnd() must not change the endContainer of the selection's original range");
assert_equals(addedRange.endOffset, endpoints[3],
"collapseToEnd() must not change the endOffset of the selection's original range");
}, "Range " + i + " " + testRanges[i] + " collapseToEnd()");
}
testDiv.style.display = "none";
</script>

View File

@ -1,97 +0,0 @@
<!doctype html>
<title>Selection.deleteFromDocument() tests</title>
<link rel=author title="Aryeh Gregor" href=ayg@aryeh.name>
<p>To debug test failures, add a query parameter with the test id (like
"?5"). Only that test will be run. Then you can look at the resulting
iframes in the DOM.
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
// We need to use explicit_done, because in Chrome 16 dev and Opera 12.00, the
// second iframe doesn't block the load event -- even though it is added before
// the load event.
setup({explicit_done: true});
// Specified by WebIDL
test(function() {
assert_equals(Selection.prototype.deleteFromDocument.length, 0,
"Selection.prototype.deleteFromDocument.length must equal 0");
}, "Selection.prototype.deleteFromDocument.length must equal 0");
testDiv.parentNode.removeChild(testDiv);
// Test an empty selection too
testRanges.unshift("empty");
var actualIframe = document.createElement("iframe");
var expectedIframe = document.createElement("iframe");
var referenceDoc = document.implementation.createHTMLDocument("");
referenceDoc.removeChild(referenceDoc.documentElement);
actualIframe.onload = function() {
expectedIframe.onload = function() {
for (var i = 0; i < testRanges.length; i++) {
if (location.search && i != Number(location.search)) {
continue;
}
test(function() {
initializeIframe(actualIframe, testRanges[i]);
initializeIframe(expectedIframe, testRanges[i]);
var actualRange = actualIframe.contentWindow.testRange;
var expectedRange = expectedIframe.contentWindow.testRange;
assert_equals(actualIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for actual deleteFromDocument");
assert_equals(expectedIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for simulated deleteFromDocument");
actualIframe.contentWindow.getSelection().removeAllRanges();
if (testRanges[i] != "empty") {
assert_equals(typeof actualRange, "object",
"Range produced in actual iframe must be an object");
assert_equals(typeof expectedRange, "object",
"Range produced in expected iframe must be an object");
assert_true(actualRange instanceof actualIframe.contentWindow.Range,
"Range produced in actual iframe must be instanceof Range");
assert_true(expectedRange instanceof expectedIframe.contentWindow.Range,
"Range produced in expected iframe must be instanceof Range");
actualIframe.contentWindow.getSelection().addRange(actualIframe.contentWindow.testRange);
expectedIframe.contentWindow.testRange.deleteContents();
}
actualIframe.contentWindow.getSelection().deleteFromDocument();
assertNodesEqual(actualIframe.contentDocument, expectedIframe.contentDocument, "DOM contents");
}, "Range " + i + ": " + testRanges[i]);
}
actualIframe.style.display = "none";
expectedIframe.style.display = "none";
done();
};
expectedIframe.src = "test-iframe.html";
document.body.appendChild(expectedIframe);
referenceDoc.appendChild(actualIframe.contentDocument.documentElement.cloneNode(true));
};
actualIframe.src = "test-iframe.html";
document.body.appendChild(actualIframe);
function initializeIframe(iframe, endpoints) {
while (iframe.contentDocument.firstChild) {
iframe.contentDocument.removeChild(iframe.contentDocument.lastChild);
}
iframe.contentDocument.appendChild(iframe.contentDocument.implementation.createDocumentType("html", "", ""));
iframe.contentDocument.appendChild(referenceDoc.documentElement.cloneNode(true));
iframe.contentWindow.setupRangeTests();
if (endpoints != "empty") {
iframe.contentWindow.testRangeInput = endpoints;
iframe.contentWindow.run();
}
}
</script>

View File

@ -1,152 +0,0 @@
<!doctype html>
<title>Selection extend() tests</title>
<meta charset=utf-8>
<body>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<div id=log></div>
<script>
"use strict";
if (W3CTest.runner) {
W3CTest.runner.requestLongerTimeout(2);
}
// Also test a selection with no ranges
testRanges.unshift("[]");
/**
* We test Selections that go both forwards and backwards here. In the latter
* case we need to use extend() to force it to go backwards, which is fair
* enough, since that's what we're testing. We test collapsed selections only
* once.
*/
for (var i = 0; i < testRanges.length; i++) {
var endpoints = eval(testRanges[i]);
for (var j = 0; j < testPoints.length; j++) {
if (endpoints[0] == endpoints[2]
&& endpoints[1] == endpoints[3]) {
// Test collapsed selections only once
test(function() {
setSelectionForwards(endpoints);
testExtend(endpoints, eval(testPoints[j]));
}, "extend() with range " + i + " " + testRanges[i]
+ " and point " + j + " " + testPoints[j]);
} else {
test(function() {
setSelectionForwards(endpoints);
testExtend(endpoints, eval(testPoints[j]));
}, "extend() forwards with range " + i + " " + testRanges[i]
+ " and point " + j + " " + testPoints[j]);
test(function() {
setSelectionBackwards(endpoints);
testExtend(endpoints, eval(testPoints[j]));
}, "extend() backwards with range " + i + " " + testRanges[i]
+ " and point " + j + " " + testPoints[j]);
}
}
}
function testExtend(endpoints, target) {
assert_equals(getSelection().rangeCount, endpoints.length/4,
"Sanity check: rangeCount must be correct");
var node = target[0];
var offset = target[1];
// "If the context object's range is null, throw an InvalidStateError
// exception and abort these steps."
if (getSelection().rangeCount == 0) {
assert_throws("INVALID_STATE_ERR", function() {
selection.extend(node, offset);
}, "extend() when rangeCount is 0 must throw InvalidStateError");
return;
}
assert_equals(getSelection().getRangeAt(0).startContainer, endpoints[0],
"Sanity check: startContainer must be correct");
assert_equals(getSelection().getRangeAt(0).startOffset, endpoints[1],
"Sanity check: startOffset must be correct");
assert_equals(getSelection().getRangeAt(0).endContainer, endpoints[2],
"Sanity check: endContainer must be correct");
assert_equals(getSelection().getRangeAt(0).endOffset, endpoints[3],
"Sanity check: endOffset must be correct");
// "Let anchor and focus be the context object's anchor and focus, and let
// new focus be the boundary point (node, offset)."
var anchorNode = getSelection().anchorNode;
var anchorOffset = getSelection().anchorOffset;
var focusNode = getSelection().focusNode;
var focusOffset = getSelection().focusOffset;
// "Let new range be a new range."
//
// We'll always be setting either new range's start or its end to new
// focus, so we'll always throw at some point. Test that now.
//
// From DOM4's "set the start or end of a range": "If node is a doctype,
// throw an "InvalidNodeTypeError" exception and terminate these steps."
if (node.nodeType == Node.DOCUMENT_TYPE_NODE) {
assert_throws("INVALID_NODE_TYPE_ERR", function() {
selection.extend(node, offset);
}, "extend() to a doctype must throw InvalidNodeTypeError");
return;
}
// From DOM4's "set the start or end of a range": "If offset is greater
// than node's length, throw an "IndexSizeError" exception and terminate
// these steps."
//
// FIXME: We should be casting offset to an unsigned int per WebIDL. Until
// we do, we need the offset < 0 check too.
if (offset < 0 || offset > getNodeLength(node)) {
assert_throws("INDEX_SIZE_ERR", function() {
selection.extend(node, offset);
}, "extend() to an offset that's greater than node length (" + getNodeLength(node) + ") must throw IndexSizeError");
return;
}
// Now back to the editing spec.
var originalRange = getSelection().getRangeAt(0);
// "If node's root is not the same as the context object's range's root,
// set new range's start and end to (node, offset)."
//
// "Otherwise, if anchor is before or equal to new focus, set new range's
// start to anchor, then set its end to new focus."
//
// "Otherwise, set new range's start to new focus, then set its end to
// anchor."
//
// "Set the context object's range to new range."
//
// "If new focus is before anchor, set the context object's direction to
// backwards. Otherwise, set it to forwards."
//
// The upshot of all these is summed up by just testing the anchor and
// offset.
getSelection().extend(node, offset);
if (furthestAncestor(anchorNode) == furthestAncestor(node)) {
assert_equals(getSelection().anchorNode, anchorNode,
"anchorNode must not change if the node passed to extend() has the same root as the original range");
assert_equals(getSelection().anchorOffset, anchorOffset,
"anchorOffset must not change if the node passed to extend() has the same root as the original range");
} else {
assert_equals(getSelection().anchorNode, node,
"anchorNode must be the node passed to extend() if it has a different root from the original range");
assert_equals(getSelection().anchorOffset, offset,
"anchorOffset must be the offset passed to extend() if the node has a different root from the original range");
}
assert_equals(getSelection().focusNode, node,
"focusNode must be the node passed to extend()");
assert_equals(getSelection().focusOffset, offset,
"focusOffset must be the offset passed to extend()");
assert_not_equals(getSelection().getRangeAt(0), originalRange,
"extend() must replace any existing range with a new one, not mutate the existing one");
}
testDiv.style.display = "none";
</script>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<title>The getRangeAt method</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
test(function() {
var sel = getSelection();
var range = document.createRange();
sel.addRange(range);
assert_throws("INDEX_SIZE_ERR", function() { sel.getRangeAt(-1); })
assert_throws("INDEX_SIZE_ERR", function() { sel.getRangeAt(1); })
});
</script>

View File

@ -1,160 +0,0 @@
<!doctype html>
<title>getSelection() tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
"use strict";
// TODO: Figure out more places where defaultView is or is not guaranteed to be
// null, and test whether getSelection() is null.
//
// TODO: Figure out a good way to test display: none iframes.
test(function() {
// Sanity checks like this are to flag known browser bugs with clearer
// error messages, instead of throwing inscrutable exceptions.
assert_true("Selection" in window,
"Sanity check: window must have Selection property");
assert_true(window.getSelection() instanceof Selection);
}, "window.getSelection() instanceof Selection");
test(function() {
assert_equals(window.getSelection(), window.getSelection());
}, "window.getSelection() === window.getSelection()");
test(function() {
assert_true("Selection" in window,
"Sanity check: window must have Selection property");
// This sanity check (which occurs a number of times below, too) is because
// document.getSelection() is supposed to return null if defaultView is
// null, so we need to figure out whether defaultView is null or not before
// we can make correct assertions about getSelection().
assert_not_equals(document.defaultView, null,
"Sanity check: document.defaultView must not be null");
assert_equals(typeof document.getSelection(), "object",
"document.getSelection() must be an object");
assert_true(document.getSelection() instanceof Selection);
}, "document.getSelection() instanceof Selection");
test(function() {
assert_not_equals(document.defaultView, null,
"Sanity check: document.defaultView must not be null");
assert_equals(document.getSelection(), document.getSelection());
}, "document.getSelection() === document.getSelection()");
test(function() {
assert_not_equals(document.defaultView, null,
"Sanity check: document.defaultView must not be null");
assert_equals(window.getSelection(), document.getSelection());
}, "window.getSelection() === document.getSelection()");
// "Each selection is associated with a single range, which may be null and is
// initially null."
//
// "The rangeCount attribute must return 0 if the context object's range is
// null, otherwise 1."
test(function() {
assert_equals(window.getSelection().rangeCount, 0,
"window.getSelection().rangeCount must initially be 0");
assert_equals(typeof document.getSelection(), "object",
"Sanity check: document.getSelection() must be an object");
assert_equals(document.getSelection().rangeCount, 0,
"document.getSelection().rangeCount must initially be 0");
}, "Selection's range must initially be null");
test(function() {
var doc = document.implementation.createHTMLDocument("");
assert_equals(doc.defaultView, null,
"Sanity check: defaultView of created HTML document must be null");
assert_equals(doc.getSelection(), null);
}, "getSelection() on HTML document with null defaultView must be null");
test(function() {
var xmlDoc = document.implementation.createDocument(null, "", null);
assert_true("getSelection" in xmlDoc, "XML document must have getSelection()");
assert_equals(xmlDoc.defaultView, null,
"Sanity check: defaultView of created XML document must be null");
assert_equals(xmlDoc.getSelection(), null);
}, "getSelection() on XML document with null defaultView must be null");
// Run a bunch of iframe tests, once immediately after the iframe is appended
// to the document and once onload. This makes a difference, because browsers
// differ (at the time of this writing) in whether they load about:blank in
// iframes synchronously or not. Per the HTML spec, there must be a browsing
// context associated with the iframe as soon as it's appended to the document,
// so there should be a selection too.
var iframe = document.createElement("iframe");
add_completion_callback(function() {
document.body.removeChild(iframe);
});
var testDescs = [];
var testFuncs = [];
testDescs.push("window.getSelection() instanceof Selection in an iframe");
testFuncs.push(function() {
assert_true("Selection" in iframe.contentWindow,
"Sanity check: window must have Selection property");
assert_not_equals(iframe.contentWindow.document.defaultView, null,
"Sanity check: document.defaultView must not be null");
assert_not_equals(iframe.contentWindow.getSelection(), null,
"window.getSelection() must not be null");
assert_true(iframe.contentWindow.getSelection() instanceof iframe.contentWindow.Selection);
});
testDescs.push("document.getSelection() instanceof Selection in an iframe");
testFuncs.push(function() {
assert_true("Selection" in iframe.contentWindow,
"Sanity check: window must have Selection property");
assert_not_equals(iframe.contentDocument.defaultView, null,
"Sanity check: document.defaultView must not be null");
assert_not_equals(iframe.contentDocument.getSelection(), null,
"document.getSelection() must not be null");
assert_equals(typeof iframe.contentDocument.getSelection(), "object",
"document.getSelection() must be an object");
assert_true(iframe.contentDocument.getSelection() instanceof iframe.contentWindow.Selection);
});
testDescs.push("window.getSelection() === document.getSelection() in an iframe");
testFuncs.push(function() {
assert_not_equals(iframe.contentDocument.defaultView, null,
"Sanity check: document.defaultView must not be null");
assert_equals(iframe.contentWindow.getSelection(), iframe.contentDocument.getSelection());
});
testDescs.push("getSelection() inside and outside iframe must return different objects");
testFuncs.push(function() {
assert_not_equals(iframe.contentWindow.getSelection(), getSelection());
});
testDescs.push("getSelection() on HTML document with null defaultView must be null inside an iframe");
testFuncs.push(function() {
var doc = iframe.contentDocument.implementation.createHTMLDocument("");
assert_equals(doc.defaultView, null,
"Sanity check: defaultView of created HTML document must be null");
assert_equals(doc.getSelection(), null);
});
var asyncTests = [];
testDescs.forEach(function(desc) {
asyncTests.push(async_test(desc + " onload"));
});
iframe.onload = function() {
asyncTests.forEach(function(t, i) {
t.step(testFuncs[i]);
t.done();
});
};
document.body.appendChild(iframe);
testDescs.forEach(function(desc, i) {
test(testFuncs[i], desc + " immediately after appendChild");
});
</script>

View File

@ -1,41 +0,0 @@
<!doctype html>
<title>Selection interface tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/WebIDLParser.js></script>
<script src=/resources/idlharness.js></script>
<script type=text/plain>
interface Selection {
readonly attribute Node? anchorNode;
readonly attribute unsigned long anchorOffset;
readonly attribute Node? focusNode;
readonly attribute unsigned long focusOffset;
readonly attribute boolean isCollapsed;
void collapse(Node node, unsigned long offset);
void collapseToStart();
void collapseToEnd();
void extend(Node node, unsigned long offset);
void selectAllChildren(Node node);
void deleteFromDocument();
readonly attribute unsigned long rangeCount;
Range getRangeAt(unsigned long index);
void addRange(Range range);
void removeRange(Range range);
void removeAllRanges();
stringifier;
};
</script>
<script>
"use strict";
var idlArray = new IdlArray();
idlArray.add_idls(document.querySelector("script[type=text\\/plain]").textContent);
idlArray.add_objects({Selection: ['getSelection()']});
idlArray.test();
</script>

View File

@ -1,31 +0,0 @@
<!doctype html>
<title>Selection.isCollapsed tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
test(function() {
selection.removeAllRanges();
assert_true(selection.isCollapsed, "isCollapsed must be true if both anchor and focus are null");
}, "Empty selection");
for (var i = 0; i < testRanges.length; i++) {
test(function() {
selection.removeAllRanges();
var endpoints = eval(testRanges[i]);
var range = ownerDocument(endpoints[0]).createRange();
range.setStart(endpoints[0], endpoints[1]);
range.setEnd(endpoints[2], endpoints[3]);
selection.addRange(range);
assert_equals(selection.isCollapsed,
endpoints[0] === endpoints[2] && endpoints[1] === endpoints[3],
"Value of isCollapsed");
}, "Range " + i + " " + testRanges[i]);
}
testDiv.style.display = "none";
</script>

View File

@ -1,45 +0,0 @@
<!doctype html>
<title>Selection.removeAllRanges() tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
// Also test a selection with no ranges
testRanges.unshift("[]");
var range = rangeFromEndpoints([paras[0].firstChild, 0, paras[0].firstChild, 1]);
for (var i = 0; i < testRanges.length; i++) {
test(function() {
setSelectionForwards(eval(testRanges[i]));
selection.removeAllRanges();
assert_equals(selection.rangeCount, 0,
"After removeAllRanges(), rangeCount must be 0");
// Test that it's forwards
selection.addRange(range);
assert_equals(selection.anchorOffset, selection.getRangeAt(0).startOffset,
"After removeAllRanges(), addRange() must be forwards, so anchorOffset must equal startOffset rather than endOffset");
assert_equals(selection.focusOffset, selection.getRangeAt(0).endOffset,
"After removeAllRanges(), addRange() must be forwards, so focusOffset must equal endOffset rather than startOffset");
}, "Range " + i + " " + testRanges[i] + " forwards");
// Copy-pasted from above
test(function() {
setSelectionBackwards(eval(testRanges[i]));
selection.removeAllRanges();
assert_equals(selection.rangeCount, 0,
"After removeAllRanges(), rangeCount must be 0");
// Test that it's forwards
selection.addRange(range);
assert_equals(selection.anchorOffset, selection.getRangeAt(0).startOffset,
"After removeAllRanges(), addRange() must be forwards, so anchorOffset must equal startOffset rather than endOffset");
assert_equals(selection.focusOffset, selection.getRangeAt(0).endOffset,
"After removeAllRanges(), addRange() must be forwards, so focusOffset must equal endOffset rather than startOffset");
}, "Range " + i + " " + testRanges[i] + " backwards");
}
testDiv.style.display = "none";
</script>

View File

@ -1,53 +0,0 @@
<!doctype html>
<title>Selection.selectAllChildren tests</title>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=common.js></script>
<script>
"use strict";
testRanges.unshift("[]");
for (var i = 0; i < testRanges.length; i++) {
var endpoints = eval(testRanges[i]);
for (var j = 0; j < testNodes.length; j++) {
var node = eval(testNodes[j]);
test(function() {
setSelectionForwards(endpoints);
var originalRange = getSelection().rangeCount
? getSelection().getRangeAt(0)
: null;
if (node.nodeType == Node.DOCUMENT_TYPE_NODE) {
assert_throws("INVALID_NODE_TYPE_ERR", function() {
selection.selectAllChildren(node);
}, "selectAllChildren() on a DocumentType must throw InvalidNodeTypeError");
return;
}
selection.selectAllChildren(node);
// This implicitly tests that the selection is forwards, by using
// anchorOffset/focusOffset instead of getRangeAt.
assert_equals(selection.rangeCount, 1,
"After selectAllChildren, rangeCount must be 1");
assert_equals(selection.anchorNode, node,
"After selectAllChildren, anchorNode must be the given node");
assert_equals(selection.anchorOffset, 0,
"After selectAllChildren, anchorOffset must be 0");
assert_equals(selection.focusNode, node,
"After selectAllChildren, focusNode must be the given node");
assert_equals(selection.focusOffset, node.childNodes.length,
"After selectAllChildren, focusOffset must be the given node's number of children");
if (originalRange) {
assert_not_equals(getSelection().getRangeAt(0), originalRange,
"selectAllChildren must replace any existing range, not mutate it");
}
}, "Range " + i + " " + testRanges[i] + ", node " + j + " " + testNodes[j]);
}
}
testDiv.style.display = "none";
</script>

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
[DEFAULT]
support-files =
[test_event.html.json]
[test_runtest.html.json]

View File

@ -1,292 +0,0 @@
{
"Simple editable div: beforeinput event, canceled":true,
"Simple editable div: input event, canceled":true,
"Simple editable div: beforeinput event, uncanceled":true,
"Simple editable div: input event, uncanceled":true,
"Editable b: execCommand() must not throw, canceled":true,
"Editable b: beforeinput event, canceled":true,
"Editable b: input event, canceled":true,
"Editable b: execCommand() must not throw, uncanceled":true,
"Editable b: beforeinput event, uncanceled":true,
"Editable b: input event, uncanceled":true,
"Changing selection from handler: beforeinput event, canceled":true,
"Changing selection from handler: input event, canceled":true,
"Changing selection from handler: beforeinput event, uncanceled":true,
"Changing selection from handler: input event, uncanceled":true,
"Command backColor, value \"\": beforeinput event, canceled":true,
"Command backColor, value \"\": beforeinput event, uncanceled":true,
"Command backColor, value \"\": input event, uncanceled":true,
"Command backColor, value \"quasit\": beforeinput event, canceled":true,
"Command backColor, value \"quasit\": input event, canceled":true,
"Command backColor, value \"quasit\": beforeinput event, uncanceled":true,
"Command backColor, value \"quasit\": input event, uncanceled":true,
"Command backColor, value \"green\": beforeinput event, canceled":true,
"Command backColor, value \"green\": input event, canceled":true,
"Command backColor, value \"green\": beforeinput event, uncanceled":true,
"Command backColor, value \"green\": input event, uncanceled":true,
"Command createLink, value \"\": beforeinput event, canceled":true,
"Command createLink, value \"\": beforeinput event, uncanceled":true,
"Command createLink, value \"\": input event, uncanceled":true,
"Command createLink, value \"quasit\": beforeinput event, canceled":true,
"Command createLink, value \"quasit\": input event, canceled":true,
"Command createLink, value \"quasit\": beforeinput event, uncanceled":true,
"Command createLink, value \"quasit\": input event, uncanceled":true,
"Command createLink, value \"http://www.w3.org/community/editing/\": beforeinput event, canceled":true,
"Command createLink, value \"http://www.w3.org/community/editing/\": input event, canceled":true,
"Command createLink, value \"http://www.w3.org/community/editing/\": beforeinput event, uncanceled":true,
"Command createLink, value \"http://www.w3.org/community/editing/\": input event, uncanceled":true,
"Command fontName, value \"\": beforeinput event, canceled":true,
"Command fontName, value \"\": beforeinput event, uncanceled":true,
"Command fontName, value \"\": input event, uncanceled":true,
"Command fontName, value \"quasit\": beforeinput event, canceled":true,
"Command fontName, value \"quasit\": input event, canceled":true,
"Command fontName, value \"quasit\": beforeinput event, uncanceled":true,
"Command fontName, value \"quasit\": input event, uncanceled":true,
"Command fontName, value \"serif\": beforeinput event, canceled":true,
"Command fontName, value \"serif\": beforeinput event, uncanceled":true,
"Command fontName, value \"serif\": input event, uncanceled":true,
"Command fontName, value \"Helvetica\": beforeinput event, canceled":true,
"Command fontName, value \"Helvetica\": input event, canceled":true,
"Command fontName, value \"Helvetica\": beforeinput event, uncanceled":true,
"Command fontName, value \"Helvetica\": input event, uncanceled":true,
"Command fontSize, value \"\": beforeinput event, canceled":true,
"Command fontSize, value \"\": beforeinput event, uncanceled":true,
"Command fontSize, value \"\": input event, uncanceled":true,
"Command fontSize, value \"quasit\": beforeinput event, canceled":true,
"Command fontSize, value \"quasit\": beforeinput event, uncanceled":true,
"Command fontSize, value \"quasit\": input event, uncanceled":true,
"Command fontSize, value \"6\": beforeinput event, canceled":true,
"Command fontSize, value \"6\": input event, canceled":true,
"Command fontSize, value \"6\": beforeinput event, uncanceled":true,
"Command fontSize, value \"6\": input event, uncanceled":true,
"Command fontSize, value \"15px\": beforeinput event, canceled":true,
"Command fontSize, value \"15px\": input event, canceled":true,
"Command fontSize, value \"15px\": beforeinput event, uncanceled":true,
"Command fontSize, value \"15px\": input event, uncanceled":true,
"Command foreColor, value \"\": beforeinput event, canceled":true,
"Command foreColor, value \"\": beforeinput event, uncanceled":true,
"Command foreColor, value \"\": input event, uncanceled":true,
"Command foreColor, value \"quasit\": beforeinput event, canceled":true,
"Command foreColor, value \"quasit\": input event, canceled":true,
"Command foreColor, value \"quasit\": beforeinput event, uncanceled":true,
"Command foreColor, value \"quasit\": input event, uncanceled":true,
"Command foreColor, value \"green\": beforeinput event, canceled":true,
"Command foreColor, value \"green\": input event, canceled":true,
"Command foreColor, value \"green\": beforeinput event, uncanceled":true,
"Command foreColor, value \"green\": input event, uncanceled":true,
"Command hiliteColor, value \"\": beforeinput event, canceled":true,
"Command hiliteColor, value \"\": beforeinput event, uncanceled":true,
"Command hiliteColor, value \"\": input event, uncanceled":true,
"Command hiliteColor, value \"quasit\": beforeinput event, canceled":true,
"Command hiliteColor, value \"quasit\": input event, canceled":true,
"Command hiliteColor, value \"quasit\": beforeinput event, uncanceled":true,
"Command hiliteColor, value \"quasit\": input event, uncanceled":true,
"Command hiliteColor, value \"green\": beforeinput event, canceled":true,
"Command hiliteColor, value \"green\": input event, canceled":true,
"Command hiliteColor, value \"green\": beforeinput event, uncanceled":true,
"Command hiliteColor, value \"green\": input event, uncanceled":true,
"Command italic, value \"\": beforeinput event, canceled":true,
"Command italic, value \"\": input event, canceled":true,
"Command italic, value \"\": beforeinput event, uncanceled":true,
"Command italic, value \"\": input event, uncanceled":true,
"Command italic, value \"quasit\": beforeinput event, canceled":true,
"Command italic, value \"quasit\": input event, canceled":true,
"Command italic, value \"quasit\": beforeinput event, uncanceled":true,
"Command italic, value \"quasit\": input event, uncanceled":true,
"Command removeFormat, value \"\": beforeinput event, canceled":true,
"Command removeFormat, value \"\": input event, canceled":true,
"Command removeFormat, value \"\": beforeinput event, uncanceled":true,
"Command removeFormat, value \"\": input event, uncanceled":true,
"Command removeFormat, value \"quasit\": beforeinput event, canceled":true,
"Command removeFormat, value \"quasit\": input event, canceled":true,
"Command removeFormat, value \"quasit\": beforeinput event, uncanceled":true,
"Command removeFormat, value \"quasit\": input event, uncanceled":true,
"Command strikeThrough, value \"\": beforeinput event, canceled":true,
"Command strikeThrough, value \"\": input event, canceled":true,
"Command strikeThrough, value \"\": beforeinput event, uncanceled":true,
"Command strikeThrough, value \"\": input event, uncanceled":true,
"Command strikeThrough, value \"quasit\": beforeinput event, canceled":true,
"Command strikeThrough, value \"quasit\": input event, canceled":true,
"Command strikeThrough, value \"quasit\": beforeinput event, uncanceled":true,
"Command strikeThrough, value \"quasit\": input event, uncanceled":true,
"Command subscript, value \"\": beforeinput event, canceled":true,
"Command subscript, value \"\": input event, canceled":true,
"Command subscript, value \"\": beforeinput event, uncanceled":true,
"Command subscript, value \"\": input event, uncanceled":true,
"Command subscript, value \"quasit\": beforeinput event, canceled":true,
"Command subscript, value \"quasit\": input event, canceled":true,
"Command subscript, value \"quasit\": beforeinput event, uncanceled":true,
"Command subscript, value \"quasit\": input event, uncanceled":true,
"Command superscript, value \"\": beforeinput event, canceled":true,
"Command superscript, value \"\": input event, canceled":true,
"Command superscript, value \"\": beforeinput event, uncanceled":true,
"Command superscript, value \"\": input event, uncanceled":true,
"Command superscript, value \"quasit\": beforeinput event, canceled":true,
"Command superscript, value \"quasit\": input event, canceled":true,
"Command superscript, value \"quasit\": beforeinput event, uncanceled":true,
"Command superscript, value \"quasit\": input event, uncanceled":true,
"Command underline, value \"\": beforeinput event, canceled":true,
"Command underline, value \"\": input event, canceled":true,
"Command underline, value \"\": beforeinput event, uncanceled":true,
"Command underline, value \"\": input event, uncanceled":true,
"Command underline, value \"quasit\": beforeinput event, canceled":true,
"Command underline, value \"quasit\": input event, canceled":true,
"Command underline, value \"quasit\": beforeinput event, uncanceled":true,
"Command underline, value \"quasit\": input event, uncanceled":true,
"Command unlink, value \"\": beforeinput event, canceled":true,
"Command unlink, value \"\": beforeinput event, uncanceled":true,
"Command unlink, value \"\": input event, uncanceled":true,
"Command unlink, value \"quasit\": beforeinput event, canceled":true,
"Command unlink, value \"quasit\": beforeinput event, uncanceled":true,
"Command unlink, value \"quasit\": input event, uncanceled":true,
"Command delete, value \"\": beforeinput event, canceled":true,
"Command delete, value \"\": input event, canceled":true,
"Command delete, value \"\": beforeinput event, uncanceled":true,
"Command delete, value \"\": input event, uncanceled":true,
"Command delete, value \"quasit\": beforeinput event, canceled":true,
"Command delete, value \"quasit\": input event, canceled":true,
"Command delete, value \"quasit\": beforeinput event, uncanceled":true,
"Command delete, value \"quasit\": input event, uncanceled":true,
"Command formatBlock, value \"\": beforeinput event, canceled":true,
"Command formatBlock, value \"\": beforeinput event, uncanceled":true,
"Command formatBlock, value \"\": input event, uncanceled":true,
"Command formatBlock, value \"quasit\": beforeinput event, canceled":true,
"Command formatBlock, value \"quasit\": beforeinput event, uncanceled":true,
"Command formatBlock, value \"quasit\": input event, uncanceled":true,
"Command formatBlock, value \"p\": beforeinput event, canceled":true,
"Command formatBlock, value \"p\": input event, canceled":true,
"Command formatBlock, value \"p\": beforeinput event, uncanceled":true,
"Command formatBlock, value \"p\": input event, uncanceled":true,
"Command forwardDelete, value \"\": beforeinput event, canceled":true,
"Command forwardDelete, value \"\": input event, canceled":true,
"Command forwardDelete, value \"\": beforeinput event, uncanceled":true,
"Command forwardDelete, value \"\": input event, uncanceled":true,
"Command forwardDelete, value \"quasit\": beforeinput event, canceled":true,
"Command forwardDelete, value \"quasit\": input event, canceled":true,
"Command forwardDelete, value \"quasit\": beforeinput event, uncanceled":true,
"Command forwardDelete, value \"quasit\": input event, uncanceled":true,
"Command indent, value \"\": beforeinput event, canceled":true,
"Command indent, value \"\": input event, canceled":true,
"Command indent, value \"\": beforeinput event, uncanceled":true,
"Command indent, value \"\": input event, uncanceled":true,
"Command indent, value \"quasit\": beforeinput event, canceled":true,
"Command indent, value \"quasit\": input event, canceled":true,
"Command indent, value \"quasit\": beforeinput event, uncanceled":true,
"Command indent, value \"quasit\": input event, uncanceled":true,
"Command insertHorizontalRule, value \"\": beforeinput event, canceled":true,
"Command insertHorizontalRule, value \"\": input event, canceled":true,
"Command insertHorizontalRule, value \"\": beforeinput event, uncanceled":true,
"Command insertHorizontalRule, value \"\": input event, uncanceled":true,
"Command insertHorizontalRule, value \"quasit\": beforeinput event, canceled":true,
"Command insertHorizontalRule, value \"quasit\": input event, canceled":true,
"Command insertHorizontalRule, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertHorizontalRule, value \"quasit\": input event, uncanceled":true,
"Command insertHorizontalRule, value \"id\": beforeinput event, canceled":true,
"Command insertHorizontalRule, value \"id\": input event, canceled":true,
"Command insertHorizontalRule, value \"id\": beforeinput event, uncanceled":true,
"Command insertHorizontalRule, value \"id\": input event, uncanceled":true,
"Command insertHTML, value \"\": beforeinput event, canceled":true,
"Command insertHTML, value \"\": input event, canceled":true,
"Command insertHTML, value \"\": beforeinput event, uncanceled":true,
"Command insertHTML, value \"\": input event, uncanceled":true,
"Command insertHTML, value \"quasit\": beforeinput event, canceled":true,
"Command insertHTML, value \"quasit\": input event, canceled":true,
"Command insertHTML, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertHTML, value \"quasit\": input event, uncanceled":true,
"Command insertHTML, value \"<b>hi</b>\": beforeinput event, canceled":true,
"Command insertHTML, value \"<b>hi</b>\": input event, canceled":true,
"Command insertHTML, value \"<b>hi</b>\": beforeinput event, uncanceled":true,
"Command insertHTML, value \"<b>hi</b>\": input event, uncanceled":true,
"Command insertImage, value \"\": beforeinput event, canceled":true,
"Command insertImage, value \"\": beforeinput event, uncanceled":true,
"Command insertImage, value \"\": input event, uncanceled":true,
"Command insertImage, value \"quasit\": beforeinput event, canceled":true,
"Command insertImage, value \"quasit\": input event, canceled":true,
"Command insertImage, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertImage, value \"quasit\": input event, uncanceled":true,
"Command insertImage, value \"http://example.com/some-image\": beforeinput event, canceled":true,
"Command insertImage, value \"http://example.com/some-image\": input event, canceled":true,
"Command insertImage, value \"http://example.com/some-image\": beforeinput event, uncanceled":true,
"Command insertImage, value \"http://example.com/some-image\": input event, uncanceled":true,
"Command insertLineBreak, value \"\": beforeinput event, canceled":true,
"Command insertLineBreak, value \"\": beforeinput event, uncanceled":true,
"Command insertLineBreak, value \"\": input event, uncanceled":true,
"Command insertLineBreak, value \"quasit\": beforeinput event, canceled":true,
"Command insertLineBreak, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertLineBreak, value \"quasit\": input event, uncanceled":true,
"Command insertOrderedList, value \"\": beforeinput event, canceled":true,
"Command insertOrderedList, value \"\": input event, canceled":true,
"Command insertOrderedList, value \"\": beforeinput event, uncanceled":true,
"Command insertOrderedList, value \"\": input event, uncanceled":true,
"Command insertOrderedList, value \"quasit\": beforeinput event, canceled":true,
"Command insertOrderedList, value \"quasit\": input event, canceled":true,
"Command insertOrderedList, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertOrderedList, value \"quasit\": input event, uncanceled":true,
"Command insertParagraph, value \"\": beforeinput event, canceled":true,
"Command insertParagraph, value \"\": input event, canceled":true,
"Command insertParagraph, value \"\": beforeinput event, uncanceled":true,
"Command insertParagraph, value \"\": input event, uncanceled":true,
"Command insertParagraph, value \"quasit\": beforeinput event, canceled":true,
"Command insertParagraph, value \"quasit\": input event, canceled":true,
"Command insertParagraph, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertParagraph, value \"quasit\": input event, uncanceled":true,
"Command insertText, value \"\": execCommand() must not throw, canceled":true,
"Command insertText, value \"\": beforeinput event, canceled":true,
"Command insertText, value \"\": execCommand() must not throw, uncanceled":true,
"Command insertText, value \"\": beforeinput event, uncanceled":true,
"Command insertText, value \"\": input event, uncanceled":true,
"Command insertText, value \"quasit\": beforeinput event, canceled":true,
"Command insertText, value \"quasit\": input event, canceled":true,
"Command insertText, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertText, value \"quasit\": input event, uncanceled":true,
"Command insertText, value \"abc\": beforeinput event, canceled":true,
"Command insertText, value \"abc\": input event, canceled":true,
"Command insertText, value \"abc\": beforeinput event, uncanceled":true,
"Command insertText, value \"abc\": input event, uncanceled":true,
"Command insertUnorderedList, value \"\": beforeinput event, canceled":true,
"Command insertUnorderedList, value \"\": input event, canceled":true,
"Command insertUnorderedList, value \"\": beforeinput event, uncanceled":true,
"Command insertUnorderedList, value \"\": input event, uncanceled":true,
"Command insertUnorderedList, value \"quasit\": beforeinput event, canceled":true,
"Command insertUnorderedList, value \"quasit\": input event, canceled":true,
"Command insertUnorderedList, value \"quasit\": beforeinput event, uncanceled":true,
"Command insertUnorderedList, value \"quasit\": input event, uncanceled":true,
"Command justifyCenter, value \"\": beforeinput event, canceled":true,
"Command justifyCenter, value \"\": input event, canceled":true,
"Command justifyCenter, value \"\": beforeinput event, uncanceled":true,
"Command justifyCenter, value \"\": input event, uncanceled":true,
"Command justifyCenter, value \"quasit\": beforeinput event, canceled":true,
"Command justifyCenter, value \"quasit\": input event, canceled":true,
"Command justifyCenter, value \"quasit\": beforeinput event, uncanceled":true,
"Command justifyCenter, value \"quasit\": input event, uncanceled":true,
"Command justifyFull, value \"\": beforeinput event, canceled":true,
"Command justifyFull, value \"\": input event, canceled":true,
"Command justifyFull, value \"\": beforeinput event, uncanceled":true,
"Command justifyFull, value \"\": input event, uncanceled":true,
"Command justifyFull, value \"quasit\": beforeinput event, canceled":true,
"Command justifyFull, value \"quasit\": input event, canceled":true,
"Command justifyFull, value \"quasit\": beforeinput event, uncanceled":true,
"Command justifyFull, value \"quasit\": input event, uncanceled":true,
"Command justifyLeft, value \"\": beforeinput event, canceled":true,
"Command justifyLeft, value \"\": input event, canceled":true,
"Command justifyLeft, value \"\": beforeinput event, uncanceled":true,
"Command justifyLeft, value \"\": input event, uncanceled":true,
"Command justifyLeft, value \"quasit\": beforeinput event, canceled":true,
"Command justifyLeft, value \"quasit\": input event, canceled":true,
"Command justifyLeft, value \"quasit\": beforeinput event, uncanceled":true,
"Command justifyLeft, value \"quasit\": input event, uncanceled":true,
"Command justifyRight, value \"\": beforeinput event, canceled":true,
"Command justifyRight, value \"\": input event, canceled":true,
"Command justifyRight, value \"\": beforeinput event, uncanceled":true,
"Command justifyRight, value \"\": input event, uncanceled":true,
"Command justifyRight, value \"quasit\": beforeinput event, canceled":true,
"Command justifyRight, value \"quasit\": input event, canceled":true,
"Command justifyRight, value \"quasit\": beforeinput event, uncanceled":true,
"Command justifyRight, value \"quasit\": input event, uncanceled":true,
"Command outdent, value \"\": beforeinput event, canceled":true,
"Command outdent, value \"\": beforeinput event, uncanceled":true,
"Command outdent, value \"\": input event, uncanceled":true,
"Command outdent, value \"quasit\": beforeinput event, canceled":true,
"Command outdent, value \"quasit\": beforeinput event, uncanceled":true,
"Command outdent, value \"quasit\": input event, uncanceled":true
}

View File

@ -1,13 +0,0 @@
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
[DEFAULT]
support-files =
[test_Document-open.html.json]
[test_addRange.html.json]
[test_collapse.html.json]
[test_collapseToStartEnd.html.json]
[test_extend.html.json]
[test_getSelection.html.json]
[test_removeAllRanges.html.json]
[test_selectAllChildren.html.json]

View File

@ -1,3 +0,0 @@
{
"Selection must be replaced with a new object after document.open()":true
}

View File

@ -1,44 +0,0 @@
{
"Range 12 [foreignPara1.firstChild, 0, foreignPara1.firstChild, 0] collapseToStart()":true,
"Range 12 [foreignPara1.firstChild, 0, foreignPara1.firstChild, 0] collapseToEnd()":true,
"Range 13 [foreignPara1.firstChild, 0, foreignPara1.firstChild, 1] collapseToStart()":true,
"Range 13 [foreignPara1.firstChild, 0, foreignPara1.firstChild, 1] collapseToEnd()":true,
"Range 14 [foreignPara1.firstChild, 2, foreignPara1.firstChild, 8] collapseToStart()":true,
"Range 14 [foreignPara1.firstChild, 2, foreignPara1.firstChild, 8] collapseToEnd()":true,
"Range 20 [foreignDoc.documentElement, 0, foreignDoc.documentElement, 1] collapseToStart()":true,
"Range 20 [foreignDoc.documentElement, 0, foreignDoc.documentElement, 1] collapseToEnd()":true,
"Range 21 [foreignDoc.head, 1, foreignDoc.head, 1] collapseToStart()":true,
"Range 21 [foreignDoc.head, 1, foreignDoc.head, 1] collapseToEnd()":true,
"Range 22 [foreignDoc.body, 0, foreignDoc.body, 0] collapseToStart()":true,
"Range 22 [foreignDoc.body, 0, foreignDoc.body, 0] collapseToEnd()":true,
"Range 34 [foreignDoc.documentElement, 1, foreignDoc.body, 0] collapseToStart()":true,
"Range 34 [foreignDoc.documentElement, 1, foreignDoc.body, 0] collapseToEnd()":true,
"Range 41 [foreignDoc, 0, foreignDoc, 0] collapseToStart()":true,
"Range 41 [foreignDoc, 0, foreignDoc, 0] collapseToEnd()":true,
"Range 42 [foreignDoc, 1, foreignComment, 2] collapseToStart()":true,
"Range 42 [foreignDoc, 1, foreignComment, 2] collapseToEnd()":true,
"Range 43 [foreignDoc.body, 0, foreignTextNode, 36] collapseToStart()":true,
"Range 43 [foreignDoc.body, 0, foreignTextNode, 36] collapseToEnd()":true,
"Range 44 [xmlDoc, 0, xmlDoc, 0] collapseToStart()":true,
"Range 44 [xmlDoc, 0, xmlDoc, 0] collapseToEnd()":true,
"Range 45 [xmlDoc, 1, xmlComment, 0] collapseToStart()":true,
"Range 45 [xmlDoc, 1, xmlComment, 0] collapseToEnd()":true,
"Range 47 [detachedForeignTextNode, 7, detachedForeignTextNode, 7] collapseToStart()":true,
"Range 47 [detachedForeignTextNode, 7, detachedForeignTextNode, 7] collapseToEnd()":true,
"Range 48 [detachedForeignTextNode, 0, detachedForeignTextNode, 8] collapseToStart()":true,
"Range 48 [detachedForeignTextNode, 0, detachedForeignTextNode, 8] collapseToEnd()":true,
"Range 49 [detachedXmlTextNode, 7, detachedXmlTextNode, 7] collapseToStart()":true,
"Range 49 [detachedXmlTextNode, 7, detachedXmlTextNode, 7] collapseToEnd()":true,
"Range 50 [detachedXmlTextNode, 0, detachedXmlTextNode, 8] collapseToStart()":true,
"Range 50 [detachedXmlTextNode, 0, detachedXmlTextNode, 8] collapseToEnd()":true,
"Range 53 [detachedForeignComment, 0, detachedForeignComment, 1] collapseToStart()":true,
"Range 53 [detachedForeignComment, 0, detachedForeignComment, 1] collapseToEnd()":true,
"Range 54 [detachedForeignComment, 4, detachedForeignComment, 4] collapseToStart()":true,
"Range 54 [detachedForeignComment, 4, detachedForeignComment, 4] collapseToEnd()":true,
"Range 55 [detachedXmlComment, 2, detachedXmlComment, 6] collapseToStart()":true,
"Range 55 [detachedXmlComment, 2, detachedXmlComment, 6] collapseToEnd()":true,
"Range 57 [foreignDocfrag, 0, foreignDocfrag, 0] collapseToStart()":true,
"Range 57 [foreignDocfrag, 0, foreignDocfrag, 0] collapseToEnd()":true,
"Range 58 [xmlDocfrag, 0, xmlDocfrag, 0] collapseToStart()":true,
"Range 58 [xmlDocfrag, 0, xmlDocfrag, 0] collapseToEnd()":true
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
{
"getSelection() on HTML document with null defaultView must be null":true,
"getSelection() on XML document with null defaultView must be null":true,
"window.getSelection() instanceof Selection in an iframe immediately after appendChild":true,
"document.getSelection() instanceof Selection in an iframe immediately after appendChild":true,
"getSelection() on HTML document with null defaultView must be null inside an iframe onload":true
}

View File

@ -1,23 +0,0 @@
{
"Range 12 [foreignPara1.firstChild, 0, foreignPara1.firstChild, 0] backwards":true,
"Range 13 [foreignPara1.firstChild, 0, foreignPara1.firstChild, 1] backwards":true,
"Range 14 [foreignPara1.firstChild, 2, foreignPara1.firstChild, 8] backwards":true,
"Range 20 [foreignDoc.documentElement, 0, foreignDoc.documentElement, 1] backwards":true,
"Range 21 [foreignDoc.head, 1, foreignDoc.head, 1] backwards":true,
"Range 22 [foreignDoc.body, 0, foreignDoc.body, 0] backwards":true,
"Range 34 [foreignDoc.documentElement, 1, foreignDoc.body, 0] backwards":true,
"Range 41 [foreignDoc, 0, foreignDoc, 0] backwards":true,
"Range 42 [foreignDoc, 1, foreignComment, 2] backwards":true,
"Range 43 [foreignDoc.body, 0, foreignTextNode, 36] backwards":true,
"Range 44 [xmlDoc, 0, xmlDoc, 0] backwards":true,
"Range 45 [xmlDoc, 1, xmlComment, 0] backwards":true,
"Range 47 [detachedForeignTextNode, 7, detachedForeignTextNode, 7] backwards":true,
"Range 48 [detachedForeignTextNode, 0, detachedForeignTextNode, 8] backwards":true,
"Range 49 [detachedXmlTextNode, 7, detachedXmlTextNode, 7] backwards":true,
"Range 50 [detachedXmlTextNode, 0, detachedXmlTextNode, 8] backwards":true,
"Range 53 [detachedForeignComment, 0, detachedForeignComment, 1] backwards":true,
"Range 54 [detachedForeignComment, 4, detachedForeignComment, 4] backwards":true,
"Range 55 [detachedXmlComment, 2, detachedXmlComment, 6] backwards":true,
"Range 57 [foreignDocfrag, 0, foreignDocfrag, 0] backwards":true,
"Range 58 [xmlDocfrag, 0, xmlDocfrag, 0] backwards":true
}

View File

@ -1,6 +0,0 @@
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
[DEFAULT]
support-files =
[test_exceptions.html.json]

View File

@ -1,6 +0,0 @@
{
"exception.hasOwnProperty(\"message\")": true,
"Object.getOwnPropertyDescriptor(exception, \"message\")": true,
"In iframe: exception.hasOwnProperty(\"message\")": true,
"In iframe: Object.getOwnPropertyDescriptor(exception, \"message\")": true
}

View File

@ -1,6 +0,0 @@
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
[DEFAULT]
support-files =
[test_DOMTokenList-stringifier.html.json]

View File

@ -1,3 +0,0 @@
{
"DOMTokenList stringifier": true
}

View File

@ -1,7 +0,0 @@
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
[DEFAULT]
support-files =
[test_historical.html.json]
[test_interfaces.html.json]

View File

@ -1,12 +0,0 @@
{
"Created element's namespace in empty.xml": true,
"Created element's namespace in xhtml.xml": true,
"Created element's namespace in svg.xml": true,
"Created element's namespace in mathml.xml": true,
"Created element's namespace in minimal_html.xml": true,
"Created element's namespace in xhtml_ns_removed.xml": true,
"Created element's namespace in xhtml_ns_changed.xml": true,
"Created element's namespace in bare_xhtml.xml": true,
"Created element's namespace in bare_svg.xml": true,
"Created element's namespace in bare_mathml.xml": true
}

View File

@ -1,3 +0,0 @@
{
"Invalid Name: \u0300foo": true
}

View File

@ -1,4 +0,0 @@
{
"Document.getElementsByTagName 1": true,
"Document.getElementsByTagName 2": true
}

View File

@ -1,7 +0,0 @@
{
"document.characterSet": true,
"foreignDoc.characterSet": true,
"xmlDoc.characterSet": true,
"xmlElement.namespaceURI": true,
"detachedXmlElement.namespaceURI": true
}

Some files were not shown because too many files have changed in this diff Show More