mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-21 09:49:14 +00:00
Merge inbound to central, a=merge
MozReview-Commit-ID: 4hSCKRdSJmM
This commit is contained in:
commit
6248f1e33c
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
27
accessible/base/AccessibleOrProxy.cpp
Normal file
27
accessible/base/AccessibleOrProxy.cpp
Normal 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();
|
||||
}
|
@ -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; }
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -26,6 +26,7 @@ if CONFIG['MOZ_DEBUG']:
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AccessibleOrProxy.cpp',
|
||||
'AccEvent.cpp',
|
||||
'AccGroupInfo.cpp',
|
||||
'AccIterator.cpp',
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -6,3 +6,4 @@ support-files =
|
||||
|
||||
[browser_usercontext.js]
|
||||
[browser_windowName.js]
|
||||
[browser_windowOpen.js]
|
||||
|
@ -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);
|
||||
});
|
@ -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=
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'.");
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
});
|
@ -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
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
});
|
@ -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");
|
||||
}
|
||||
});
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
#define CREATE_DIRECTORY_TASK_PERMISSION "create"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -112,6 +112,9 @@ public:
|
||||
already_AddRefed<Promise>
|
||||
GetFilesAndDirectories(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetFiles(bool aRecursiveFlag, ErrorResult& aRv);
|
||||
|
||||
// =========== End WebIDL bindings.============
|
||||
|
||||
/**
|
||||
|
@ -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: {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
359
dom/filesystem/GetFilesTask.cpp
Normal file
359
dom/filesystem/GetFilesTask.cpp
Normal 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
|
99
dom/filesystem/GetFilesTask.h
Normal file
99
dom/filesystem/GetFilesTask.h
Normal 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
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
#define REMOVE_TASK_PERMISSION "write"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -28,6 +28,7 @@ UNIFIED_SOURCES += [
|
||||
'FileSystemUtils.cpp',
|
||||
'GetDirectoryListingTask.cpp',
|
||||
'GetFileOrDirectoryTask.cpp',
|
||||
'GetFilesTask.cpp',
|
||||
'OSFileSystem.cpp',
|
||||
'RemoveTask.cpp',
|
||||
]
|
||||
|
75
dom/filesystem/tests/filesystem_commons.js
Normal file
75
dom/filesystem/tests/filesystem_commons.js
Normal 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);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
filesystem_commons.js
|
||||
script_fileList.js
|
||||
worker_basic.js
|
||||
|
||||
|
@ -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", {
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
hg|https://dvcs.w3.org/hg/editing|editing
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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>
|
@ -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>
|
@ -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
@ -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>Äb̈c̈d̈ëf̈g̈ḧ\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]);
|
||||
}
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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
@ -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]
|
@ -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
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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]
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"Selection must be replaced with a new object after document.open()":true
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
@ -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
|
||||
}
|
@ -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
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +0,0 @@
|
||||
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
|
||||
|
||||
[test_exceptions.html.json]
|
@ -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
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
|
||||
|
||||
[test_DOMTokenList-stringifier.html.json]
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"DOMTokenList stringifier": true
|
||||
}
|
@ -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]
|
@ -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
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"Invalid Name: \u0300foo": true
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Document.getElementsByTagName 1": true,
|
||||
"Document.getElementsByTagName 2": true
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user