'use strict';
/** Moves the mouse to the center of |element|. */
const mouseMoveToCenter = element => {
const clientRect = element.getBoundingClientRect();
const centerX = (clientRect.left + clientRect.right) / 2;
const centerY = (clientRect.top + clientRect.bottom) / 2;
if (window.eventSender)
eventSender.mouseMoveTo(centerX, centerY);
};
/**
* Recursively loads content into a series of nested iframes.
* Returns a Promise that resolves with the HTMLDocument of the innermost frame.
*/
const loadNestedFrames = async domRoot => {
const frame = domRoot.querySelector('iframe');
if (!frame)
return domRoot;
const htmlSourceId = frame.getAttribute('data-source');
frame.srcdoc = document.getElementById(htmlSourceId).textContent;
const frameLoaded = new Promise(resolve => { frame.onload = resolve; });
await frameLoaded;
return await loadNestedFrames(frame.contentDocument);
};
/** Retrieves an element with id in an arbitrarily deep nesting of iframes. */
const getElementByIdAcrossIframes = (domRoot, id) => {
if (!domRoot)
return null;
const dragBox = domRoot.getElementById(id);
if (dragBox)
return dragBox;
return getElementByIdAcrossIframes(
domRoot.querySelector('iframe').contentDocument, id);
};
const loadIframe = async () => {
await loadNestedFrames(document);
return document.getElementById('outer-iframe');
};
const loadImage = async () => {
const image = document.createElement('img');
image.src = '../resources/greenbox.png';
const imageLoaded = new Promise(resolve => {
image.onload = resolve(image);
document.getElementById('moved-item-source').appendChild(image);
});
return await imageLoaded;
};
const loadMovedItem = async loadItem => {
if (loadItem.includes('iframe'))
return await loadIframe();
else if (loadItem.includes('image'))
return await loadImage();
};
/**
* Test if moving an element (iframe or image) will cancel dragging by
* resetting drag source.
* testCase: a testCase to test, containing a specified type to load and
* whether or not dragend is expected to fire, as well as the action
* to attempt.
*/
const dragDomMoveTest = testCase => {
promise_test(async t => {
document.querySelector('#test-description').textContent =
JSON.stringify(testCase);
const gotEvent = {
dragStart: false,
dragOver: false,
drop: false,
dragEnd: false,
};
let movedItem = await loadMovedItem(testCase.load);
const dragBox = getElementByIdAcrossIframes(document, 'drag-box');
const dropBox = document.getElementById('drop-box');
const doneButton = document.createElement('button');
dragBox.ondragstart = t.step_func(e => {
gotEvent.dragStart = true;
e.dataTransfer.setData('text/plain', 'Needed to work in Firefox');
});
dropBox.ondragover = t.step_func(e => {
gotEvent.dragOver = true;
e.preventDefault();
});
const dndTest = new Promise(resolve => {
dragBox.ondragend = t.step_func(e => {
gotEvent.dragEnd = true;
return resolve();
});
dropBox.ondrop = t.step_func(async e => {
gotEvent.drop = true;
e.preventDefault();
const movedItemDestination =
document.getElementById('moved-item-destination');
const movedItemSource =
document.getElementById('moved-item-source');
// Test whether dragging away or detaching movedItem
// will disable dragging.
if (testCase.action == 'removeChild')
movedItem = movedItem.parentNode.removeChild(movedItem);
else if (testCase.action == 'appendChild')
movedItemDestination.appendChild(movedItem);
else
return reject('Error: Invalid testCase.action. Please make sure the testCase is spelled correctly');
// Click to resolve test as backup in case dragend never triggers to
// end the test.
setTimeout(() => {
const clickEvent = new Event('click');
doneButton.dispatchEvent(clickEvent);
}, 100);
// Reset iframe location to teardown and prep for next test.
if (testCase.load.includes('iframe')) {
const movedItemLoaded = new Promise(resolve => {
movedItem.onload = resolve;
setTimeout(() => { movedItemSource.appendChild(movedItem); }, 100);
});
await movedItemLoaded;
}
});
doneButton.onclick = t.step_func(() => {
return resolve();
})
// Do drag and drop.
if (window.eventSender) {
mouseMoveToCenter(dragBox);
eventSender.mouseDown();
setTimeout(() => {
mouseMoveToCenter(dropBox);
eventSender.mouseUp();
}, 100);
}
});
await dndTest;
assert_true(gotEvent.dragStart,
'drag-box should have gotten a dragstart event');
assert_true(gotEvent.dragOver,
'drop-box should have gotten a dragover event');
assert_true(gotEvent.drop,
'drop-box should have gotten a drop event');
assert_equals(gotEvent.dragEnd, testCase.expectDragEnd,
'drag-box should have gotten a dragEnd event');
}, `tested with input: ${testCase.load}, ${testCase.action}`);
};
const dragDomMoveTests = testCases => {
for (let testCase of testCases)
dragDomMoveTest(testCase);
promise_test(() => {
return Promise.resolve().then(() => {
document.querySelector('#test-description').textContent = 'done';
});
}, 'all tests complete');
}