1593static bool processPauseAction(JSON::Object& actionItem, Action& action, std::optional<String>& errorMessage)
1594{
1595 RefPtr<JSON::Value> durationValue;
1596 if (!actionItem.getValue(ASCIILiteral("duration"), durationValue)) {
1597 errorMessage = String("The parameter 'duration' is missing in pause action");
1598 return false;
1599 }
1600
1601 auto duration = unsignedValue(*durationValue);
1602 if (!duration) {
1603 errorMessage = String("The parameter 'duration' is invalid in pause action");
1604 return false;
1605 }
1606
1607 action.duration = duration.value();
1608 return true;
1609}
1610
1611static std::optional<Action> processNullAction(const String& id, JSON::Object& actionItem, std::optional<String>& errorMessage)
1612{
1613 String subtype;
1614 actionItem.getString(ASCIILiteral("type"), subtype);
1615 if (subtype != "pause") {
1616 errorMessage = String("The parameter 'type' in null action is invalid or missing");
1617 return std::nullopt;
1618 }
1619
1620 Action action(id, Action::Type::None, Action::Subtype::Pause);
1621 if (!processPauseAction(actionItem, action, errorMessage))
1622 return std::nullopt;
1623
1624 return action;
1625}
1626
1627static std::optional<Action> processKeyAction(const String& id, JSON::Object& actionItem, std::optional<String>& errorMessage)
1628{
1629 Action::Subtype actionSubtype;
1630 String subtype;
1631 actionItem.getString(ASCIILiteral("type"), subtype);
1632 if (subtype == "pause")
1633 actionSubtype = Action::Subtype::Pause;
1634 else if (subtype == "keyUp")
1635 actionSubtype = Action::Subtype::KeyUp;
1636 else if (subtype == "keyDown")
1637 actionSubtype = Action::Subtype::KeyDown;
1638 else {
1639 errorMessage = String("The parameter 'type' of key action is invalid");
1640 return std::nullopt;
1641 }
1642
1643 Action action(id, Action::Type::Key, actionSubtype);
1644
1645 switch (actionSubtype) {
1646 case Action::Subtype::Pause:
1647 if (!processPauseAction(actionItem, action, errorMessage))
1648 return std::nullopt;
1649 break;
1650 case Action::Subtype::KeyUp:
1651 case Action::Subtype::KeyDown: {
1652 RefPtr<JSON::Value> keyValue;
1653 if (!actionItem.getValue(ASCIILiteral("value"), keyValue)) {
1654 errorMessage = String("The paramater 'value' is missing for key up/down action");
1655 return std::nullopt;
1656 }
1657 String key;
1658 if (!keyValue->asString(key) || key.isEmpty()) {
1659 errorMessage = String("The paramater 'value' is invalid for key up/down action");
1660 return std::nullopt;
1661 }
1662 // FIXME: check single unicode code point.
1663 action.key = key;
1664 break;
1665 }
1666 case Action::Subtype::PointerUp:
1667 case Action::Subtype::PointerDown:
1668 case Action::Subtype::PointerMove:
1669 case Action::Subtype::PointerCancel:
1670 ASSERT_NOT_REACHED();
1671 }
1672
1673 return action;
1674}
1675
1676static MouseButton actionMouseButton(unsigned button)
1677{
1678 // MouseEvent.button
1679 // https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-button-1
1680 switch (button) {
1681 case 0:
1682 return MouseButton::Left;
1683 case 1:
1684 return MouseButton::Middle;
1685 case 2:
1686 return MouseButton::Right;
1687 }
1688
1689 return MouseButton::None;
1690}
1691
1692static std::optional<Action> processPointerAction(const String& id, PointerParameters& parameters, JSON::Object& actionItem, std::optional<String>& errorMessage)
1693{
1694 Action::Subtype actionSubtype;
1695 String subtype;
1696 actionItem.getString(ASCIILiteral("type"), subtype);
1697 if (subtype == "pause")
1698 actionSubtype = Action::Subtype::Pause;
1699 else if (subtype == "pointerUp")
1700 actionSubtype = Action::Subtype::PointerUp;
1701 else if (subtype == "pointerDown")
1702 actionSubtype = Action::Subtype::PointerDown;
1703 else if (subtype == "pointerMove")
1704 actionSubtype = Action::Subtype::PointerMove;
1705 else if (subtype == "pointerCancel")
1706 actionSubtype = Action::Subtype::PointerCancel;
1707 else {
1708 errorMessage = String("The parameter 'type' of pointer action is invalid");
1709 return std::nullopt;
1710 }
1711
1712 Action action(id, Action::Type::Pointer, actionSubtype);
1713 action.pointerType = parameters.pointerType;
1714
1715 switch (actionSubtype) {
1716 case Action::Subtype::Pause:
1717 if (!processPauseAction(actionItem, action, errorMessage))
1718 return std::nullopt;
1719 break;
1720 case Action::Subtype::PointerUp:
1721 case Action::Subtype::PointerDown: {
1722 RefPtr<JSON::Value> buttonValue;
1723 if (!actionItem.getValue(ASCIILiteral("button"), buttonValue)) {
1724 errorMessage = String("The paramater 'button' is missing for pointer up/down action");
1725 return std::nullopt;
1726 }
1727 auto button = unsignedValue(*buttonValue);
1728 if (!button) {
1729 errorMessage = String("The paramater 'button' is invalid for pointer up/down action");
1730 return std::nullopt;
1731 }
1732 action.button = actionMouseButton(button.value());
1733 break;
1734 }
1735 case Action::Subtype::PointerMove: {
1736 RefPtr<JSON::Value> durationValue;
1737 if (actionItem.getValue(ASCIILiteral("duration"), durationValue)) {
1738 auto duration = unsignedValue(*durationValue);
1739 if (!duration) {
1740 errorMessage = String("The parameter 'duration' is invalid in pointer move action");
1741 return std::nullopt;
1742 }
1743 action.duration = duration.value();
1744 }
1745
1746 RefPtr<JSON::Value> originValue;
1747 if (actionItem.getValue(ASCIILiteral("origin"), originValue)) {
1748 if (originValue->type() == JSON::Value::Type::Object) {
1749 RefPtr<JSON::Object> originObject;
1750 originValue->asObject(originObject);
1751 String elementID;
1752 if (!originObject->getString(Session::webElementIdentifier(), elementID)) {
1753 errorMessage = String("The parameter 'origin' is not a valid web element object in pointer move action");
1754 return std::nullopt;
1755 }
1756 action.origin = PointerOrigin { PointerOrigin::Type::Element, elementID };
1757 } else {
1758 String origin;
1759 originValue->asString(origin);
1760 if (origin == "viewport")
1761 action.origin = PointerOrigin { PointerOrigin::Type::Viewport, std::nullopt };
1762 else if (origin == "pointer")
1763 action.origin = PointerOrigin { PointerOrigin::Type::Pointer, std::nullopt };
1764 else {
1765 errorMessage = String("The parameter 'origin' is invalid in pointer move action");
1766 return std::nullopt;
1767 }
1768 }
1769 } else
1770 action.origin = PointerOrigin { PointerOrigin::Type::Viewport, std::nullopt };
1771
1772 RefPtr<JSON::Value> xValue;
1773 if (actionItem.getValue(ASCIILiteral("x"), xValue)) {
1774 auto x = valueAsNumberInRange(*xValue, INT_MIN);
1775 if (!x) {
1776 errorMessage = String("The paramater 'x' is invalid for pointer move action");
1777 return std::nullopt;
1778 }
1779 action.x = x.value();
1780 }
1781
1782 RefPtr<JSON::Value> yValue;
1783 if (actionItem.getValue(ASCIILiteral("y"), yValue)) {
1784 auto y = valueAsNumberInRange(*yValue, INT_MIN);
1785 if (!y) {
1786 errorMessage = String("The paramater 'y' is invalid for pointer move action");
1787 return std::nullopt;
1788 }
1789 action.y = y.value();
1790 }
1791 break;
1792 }
1793 case Action::Subtype::PointerCancel:
1794 break;
1795 case Action::Subtype::KeyUp:
1796 case Action::Subtype::KeyDown:
1797 ASSERT_NOT_REACHED();
1798 }
1799
1800 return action;
1801}
1802
1803static std::optional<PointerParameters> processPointerParameters(JSON::Object& actionSequence, std::optional<String>& errorMessage)
1804{
1805 PointerParameters parameters;
1806 RefPtr<JSON::Value> parametersDataValue;
1807 if (!actionSequence.getValue(ASCIILiteral("parameters"), parametersDataValue))
1808 return parameters;
1809
1810 RefPtr<JSON::Object> parametersData;
1811 if (!parametersDataValue->asObject(parametersData)) {
1812 errorMessage = String("Action sequence pointer parameters is not an object");
1813 return std::nullopt;
1814 }
1815
1816 String pointerType;
1817 if (!parametersData->getString(ASCIILiteral("pointerType"), pointerType))
1818 return parameters;
1819
1820 if (pointerType == "mouse")
1821 parameters.pointerType = PointerType::Mouse;
1822 else if (pointerType == "pen")
1823 parameters.pointerType = PointerType::Pen;
1824 else if (pointerType == "touch")
1825 parameters.pointerType = PointerType::Touch;
1826 else {
1827 errorMessage = String("The parameter 'pointerType' in action sequence pointer parameters is invalid");
1828 return std::nullopt;
1829 }
1830
1831 return parameters;
1832}
1833
1834static std::optional<Vector<Action>> processInputActionSequence(Session& session, JSON::Value& actionSequenceValue, std::optional<String>& errorMessage)
1835{
1836 RefPtr<JSON::Object> actionSequence;
1837 if (!actionSequenceValue.asObject(actionSequence)) {
1838 errorMessage = String("The action sequence is not an object");
1839 return std::nullopt;
1840 }
1841
1842 String type;
1843 actionSequence->getString(ASCIILiteral("type"), type);
1844 InputSource::Type inputSourceType;
1845 if (type == "key")
1846 inputSourceType = InputSource::Type::Key;
1847 else if (type == "pointer")
1848 inputSourceType = InputSource::Type::Pointer;
1849 else if (type == "none")
1850 inputSourceType = InputSource::Type::None;
1851 else {
1852 errorMessage = String("The parameter 'type' is invalid or missing in action sequence");
1853 return std::nullopt;
1854 }
1855
1856 String id;
1857 if (!actionSequence->getString(ASCIILiteral("id"), id)) {
1858 errorMessage = String("The parameter 'id' is invalid or missing in action sequence");
1859 return std::nullopt;
1860 }
1861
1862 std::optional<PointerParameters> parameters;
1863 std::optional<PointerType> pointerType;
1864 if (inputSourceType == InputSource::Type::Pointer) {
1865 parameters = processPointerParameters(*actionSequence, errorMessage);
1866 if (!parameters)
1867 return std::nullopt;
1868
1869 pointerType = parameters->pointerType;
1870 }
1871
1872 auto& inputSource = session.getOrCreateInputSource(id, inputSourceType, pointerType);
1873 if (inputSource.type != inputSourceType) {
1874 errorMessage = String("Action sequence type doesn't match input source type");
1875 return std::nullopt;
1876 }
1877
1878 if (inputSource.type == InputSource::Type::Pointer && inputSource.pointerType != pointerType) {
1879 errorMessage = String("Action sequence pointer type doesn't match input source pointer type");
1880 return std::nullopt;
1881 }
1882
1883 RefPtr<JSON::Array> actionItems;
1884 if (!actionSequence->getArray(ASCIILiteral("actions"), actionItems)) {
1885 errorMessage = String("The parameter 'actions' is invalid or not present in action sequence");
1886 return std::nullopt;
1887 }
1888
1889 Vector<Action> actions;
1890 unsigned actionItemsLength = actionItems->length();
1891 for (unsigned i = 0; i < actionItemsLength; ++i) {
1892 auto actionItemValue = actionItems->get(i);
1893 RefPtr<JSON::Object> actionItem;
1894 if (!actionItemValue->asObject(actionItem)) {
1895 errorMessage = String("An action in action sequence is not an object");
1896 return std::nullopt;
1897 }
1898
1899 std::optional<Action> action;
1900 if (inputSourceType == InputSource::Type::None)
1901 action = processNullAction(id, *actionItem, errorMessage);
1902 else if (inputSourceType == InputSource::Type::Key)
1903 action = processKeyAction(id, *actionItem, errorMessage);
1904 else if (inputSourceType == InputSource::Type::Pointer)
1905 action = processPointerAction(id, parameters.value(), *actionItem, errorMessage);
1906 if (!action)
1907 return std::nullopt;
1908
1909 actions.append(action.value());
1910 }
1911
1912 return actions;
1913}
1914
1915void WebDriverService::performActions(RefPtr<JSON::Object>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1916{
1917 // §17.5 Perform Actions.
1918 // https://w3c.github.io/webdriver/webdriver-spec.html#perform-actions
1919 if (!findSessionOrCompleteWithError(*parameters, completionHandler))
1920 return;
1921
1922 RefPtr<JSON::Array> actionsArray;
1923 if (!parameters->getArray(ASCIILiteral("actions"), actionsArray)) {
1924 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("The paramater 'actions' is invalid or not present")));
1925 return;
1926 }
1927
1928 std::optional<String> errorMessage;
1929 Vector<Vector<Action>> actionsByTick;
1930 unsigned actionsArrayLength = actionsArray->length();
1931 for (unsigned i = 0; i < actionsArrayLength; ++i) {
1932 auto actionSequence = actionsArray->get(i);
1933 auto inputSourceActions = processInputActionSequence(*m_session, *actionSequence, errorMessage);
1934 if (!inputSourceActions) {
1935 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, errorMessage.value()));
1936 return;
1937 }
1938 for (unsigned i = 0; i < inputSourceActions->size(); ++i) {
1939 if (actionsByTick.size() < i + 1)
1940 actionsByTick.append({ });
1941 actionsByTick[i].append(inputSourceActions.value()[i]);
1942 }
1943 }
1944
1945 m_session->performActions(WTFMove(actionsByTick), WTFMove(completionHandler));
1946}
1947
1948void WebDriverService::releaseActions(RefPtr<JSON::Object>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1949{
1950 // §17.5 Release Actions.
1951 // https://w3c.github.io/webdriver/webdriver-spec.html#release-actions
1952 if (!findSessionOrCompleteWithError(*parameters, completionHandler))
1953 return;
1954
1955 m_session->releaseActions(WTFMove(completionHandler));
1956}
1957