Changeset 220740 in webkit
- Timestamp:
- Aug 15, 2017 12:03:13 AM (7 years ago)
- Location:
- trunk/Source
- Files:
-
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r220734 r220740 1 2017-08-14 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle click events on option elements 4 https://bugs.webkit.org/show_bug.cgi?id=174710 5 <rdar://problem/33459305> 6 7 Reviewed by Brian Burg. 8 9 Export WebCore symbols required by WebKit layer. 10 11 * html/HTMLOptGroupElement.h: 12 * html/HTMLOptionElement.h: 13 1 14 2017-08-14 Simon Fraser <simon.fraser@apple.com> 2 15 -
trunk/Source/WebCore/html/HTMLOptGroupElement.h
r208179 r220740 35 35 36 36 bool isDisabledFormControl() const final; 37 HTMLSelectElement* ownerSelectElement() const;37 WEBCORE_EXPORT HTMLSelectElement* ownerSelectElement() const; 38 38 39 39 WEBCORE_EXPORT String groupLabelText() const; -
trunk/Source/WebCore/html/HTMLOptionElement.h
r217168 r220740 50 50 51 51 #if ENABLE(DATALIST_ELEMENT) 52 HTMLDataListElement* ownerDataListElement() const;52 WEBCORE_EXPORT HTMLDataListElement* ownerDataListElement() const; 53 53 #endif 54 HTMLSelectElement* ownerSelectElement() const;54 WEBCORE_EXPORT HTMLSelectElement* ownerSelectElement() const; 55 55 56 56 WEBCORE_EXPORT String label() const; … … 60 60 bool ownElementDisabled() const { return m_disabled; } 61 61 62 bool isDisabledFormControl() const final;62 WEBCORE_EXPORT bool isDisabledFormControl() const final; 63 63 64 64 String textIndentedToRespectGroupLabel() const; -
trunk/Source/WebDriver/ChangeLog
r220584 r220740 1 2017-08-14 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle click events on option elements 4 https://bugs.webkit.org/show_bug.cgi?id=174710 5 <rdar://problem/33459305> 6 7 Reviewed by Brian Burg. 8 9 Option elements are considered as a special case by the specification. When clicking an option element, we 10 should get its container and use it when scrolling into view and calculating in-view center point instead of the 11 option element itself. Then, we should not emulate a click, but change the selected status of the option element 12 like if it were done by a user action, firing the corresponding events. Now we check whether the element is an 13 option to call selectOptionElement() or performMouseInteraction(). 14 15 This fixes more than 20 selenium tests. 16 17 * CommandResult.cpp: 18 (WebDriver::CommandResult::CommandResult): Handle ElementNotSelectable protocol error. 19 (WebDriver::CommandResult::httpStatusCode const): Add ElementNotSelectable. 20 (WebDriver::CommandResult::errorString const): Ditto. 21 * CommandResult.h: 22 * Session.cpp: 23 (WebDriver::Session::selectOptionElement): Ask automation to select the given option element. 24 (WebDriver::Session::elementClick): Call selectOptionElement() or performMouseInteraction() depending on whether 25 the element is an option or not. 26 * Session.h: 27 1 28 2017-08-11 Carlos Alberto Lopez Perez <clopez@igalia.com> 2 29 -
trunk/Source/WebDriver/CommandResult.cpp
r220394 r220740 107 107 else if (errorName == "NoJavaScriptDialog") 108 108 m_errorCode = ErrorCode::NoSuchAlert; 109 else if (errorName == "ElementNotSelectable") 110 m_errorCode = ErrorCode::ElementNotSelectable; 109 111 110 112 break; … … 128 130 switch (m_errorCode.value()) { 129 131 case ErrorCode::ElementClickIntercepted: 132 case ErrorCode::ElementNotSelectable: 130 133 case ErrorCode::ElementNotInteractable: 131 134 case ErrorCode::InvalidArgument: … … 163 166 case ErrorCode::ElementClickIntercepted: 164 167 return ASCIILiteral("element click intercepted"); 168 case ErrorCode::ElementNotSelectable: 169 return ASCIILiteral("element not selectable"); 165 170 case ErrorCode::ElementNotInteractable: 166 171 return ASCIILiteral("element not interactable"); -
trunk/Source/WebDriver/CommandResult.h
r220388 r220740 42 42 enum class ErrorCode { 43 43 ElementClickIntercepted, 44 ElementNotSelectable, 44 45 ElementNotInteractable, 45 46 InvalidArgument, -
trunk/Source/WebDriver/Session.cpp
r220388 r220740 1192 1192 } 1193 1193 1194 void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) 1195 { 1196 RefPtr<InspectorObject> parameters = InspectorObject::create(); 1197 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value()); 1198 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value()); 1199 parameters->setString(ASCIILiteral("nodeHandle"), elementID); 1200 m_host->sendCommandToBackend(ASCIILiteral("selectOptionElement"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { 1201 if (response.isError) { 1202 completionHandler(CommandResult::fail(WTFMove(response.responseObject))); 1203 return; 1204 } 1205 completionHandler(CommandResult::success()); 1206 }); 1207 } 1208 1194 1209 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) 1195 1210 { … … 1201 1216 OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded; 1202 1217 options |= ElementLayoutOption::UseViewportCoordinates; 1203 computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&& inViewCenter, bool isObscured, RefPtr<InspectorObject>&& error) mutable {1218 computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&& inViewCenter, bool isObscured, RefPtr<InspectorObject>&& error) mutable { 1204 1219 if (!rect || error) { 1205 1220 completionHandler(CommandResult::fail(WTFMove(error))); … … 1215 1230 } 1216 1231 1217 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler)); 1218 1219 waitForNavigationToComplete(WTFMove(completionHandler)); 1232 getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), isObscured, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1233 bool isOptionElement = false; 1234 if (!result.isError()) { 1235 String tagName; 1236 if (result.result()->asString(tagName)) 1237 isOptionElement = tagName == "option"; 1238 } 1239 1240 Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { 1241 if (result.isError()) { 1242 completionHandler(WTFMove(result)); 1243 return; 1244 } 1245 1246 waitForNavigationToComplete(WTFMove(completionHandler)); 1247 }; 1248 if (isOptionElement) 1249 selectOptionElement(elementID, WTFMove(continueAfterClickFunction)); 1250 else 1251 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction)); 1252 }); 1220 1253 }); 1221 1254 } -
trunk/Source/WebDriver/Session.h
r220388 r220740 138 138 void computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption>, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<Inspector::InspectorObject>&&)>&&); 139 139 140 void selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&&); 141 140 142 enum class MouseButton { None, Left, Middle, Right }; 141 143 enum class MouseInteraction { Move, Down, Up, SingleClick, DoubleClick }; -
trunk/Source/WebKit/ChangeLog
r220734 r220740 1 2017-08-14 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 WebDriver: handle click events on option elements 4 https://bugs.webkit.org/show_bug.cgi?id=174710 5 <rdar://problem/33459305> 6 7 Reviewed by Brian Burg. 8 9 Add selectOptionElement method to automation to select an option element according to the WebDriver 10 specification. 11 12 14.1 Element Click. 13 https://w3c.github.io/webdriver/webdriver-spec.html#element-click 14 15 * UIProcess/Automation/Automation.json: Add selectOptionElement method and ElementNotSelectable error. 16 * UIProcess/Automation/WebAutomationSession.cpp: 17 (WebKit::WebAutomationSession::selectOptionElement):Send SelectOptionElement message to the web process. 18 (WebKit::WebAutomationSession::didSelectOptionElement): Notify the driver. 19 * UIProcess/Automation/WebAutomationSession.h: 20 * UIProcess/Automation/WebAutomationSession.messages.in: Add DidSelectOptionElement message. 21 * WebProcess/Automation/WebAutomationSessionProxy.cpp: 22 (WebKit::elementContainer): Helper to get the container of an element according to the spec. 23 (WebKit::WebAutomationSessionProxy::computeElementLayout): Use the container element to scroll the view and 24 compute the in-view center point. 25 (WebKit::WebAutomationSessionProxy::selectOptionElement): Use HTMLSelectElement::optionSelectedByUser(). 26 * WebProcess/Automation/WebAutomationSessionProxy.h: 27 * WebProcess/Automation/WebAutomationSessionProxy.messages.in: Add SelectOptionElement message. 28 1 29 2017-08-14 Simon Fraser <simon.fraser@apple.com> 2 30 -
trunk/Source/WebKit/UIProcess/Automation/Automation.json
r220394 r220740 60 60 "InvalidParameter", 61 61 "InvalidSelector", 62 "ElementNotInteractable" 62 "ElementNotInteractable", 63 "ElementNotSelectable" 63 64 ] 64 65 }, … … 431 432 }, 432 433 { 434 "name": "selectOptionElement", 435 "description": "Selects the given option element. In case of container with multiple options enabled, the element selectedness is toggled.", 436 "parameters": [ 437 { "name": "browsingContextHandle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context." }, 438 { "name": "frameHandle", "$ref": "FrameHandle", "description": "The handle for the frame that contains the element." }, 439 { "name": "nodeHandle", "$ref": "NodeHandle", "description": "The handle of the element to use." } 440 ], 441 "async": true 442 }, 443 { 433 444 "name": "isShowingJavaScriptDialog", 434 445 "description": "Checks if a browsing context is showing a JavaScript alert, confirm, or prompt dialog.", -
trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp
r220394 r220740 846 846 } 847 847 848 void WebAutomationSession::selectOptionElement(Inspector::ErrorString& errorString, const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, Ref<SelectOptionElementCallback>&& callback) 849 { 850 WebPageProxy* page = webPageProxyForHandle(browsingContextHandle); 851 if (!page) 852 FAIL_WITH_PREDEFINED_ERROR(WindowNotFound); 853 854 std::optional<uint64_t> frameID = webFrameIDForHandle(frameHandle); 855 if (!frameID) 856 FAIL_WITH_PREDEFINED_ERROR(FrameNotFound); 857 858 uint64_t callbackID = m_nextSelectOptionElementCallbackID++; 859 m_selectOptionElementCallbacks.set(callbackID, WTFMove(callback)); 860 861 page->process().send(Messages::WebAutomationSessionProxy::SelectOptionElement(page->pageID(), frameID.value(), nodeHandle, callbackID), 0); 862 } 863 864 void WebAutomationSession::didSelectOptionElement(uint64_t callbackID, const String& errorType) 865 { 866 auto callback = m_selectOptionElementCallbacks.take(callbackID); 867 if (!callback) 868 return; 869 870 if (!errorType.isEmpty()) { 871 callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType)); 872 return; 873 } 874 875 callback->sendSuccess(); 876 } 877 878 848 879 void WebAutomationSession::isShowingJavaScriptDialog(Inspector::ErrorString& errorString, const String& browsingContextHandle, bool* result) 849 880 { -
trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h
r220317 r220740 134 134 void resolveParentFrameHandle(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, Ref<ResolveParentFrameHandleCallback>&&) override; 135 135 void computeElementLayout(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const bool* optionalScrollIntoViewIfNeeded, const bool* useViewportCoordinates, Ref<Inspector::AutomationBackendDispatcherHandler::ComputeElementLayoutCallback>&&) override; 136 void selectOptionElement(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, Ref<Inspector::AutomationBackendDispatcherHandler::SelectOptionElementCallback>&&) override; 136 137 void isShowingJavaScriptDialog(Inspector::ErrorString&, const String& browsingContextHandle, bool* result) override; 137 138 void dismissCurrentJavaScriptDialog(Inspector::ErrorString&, const String& browsingContextHandle) override; … … 177 178 void didResolveParentFrame(uint64_t callbackID, uint64_t frameID, const String& errorType); 178 179 void didComputeElementLayout(uint64_t callbackID, WebCore::IntRect, std::optional<WebCore::IntPoint>, bool isObscured, const String& errorType); 180 void didSelectOptionElement(uint64_t callbackID, const String& errorType); 179 181 void didTakeScreenshot(uint64_t callbackID, const ShareableBitmap::Handle&, const String& errorType); 180 182 void didGetCookiesForFrame(uint64_t callbackID, Vector<WebCore::Cookie>, const String& errorType); … … 242 244 HashMap<uint64_t, RefPtr<Inspector::AutomationBackendDispatcherHandler::DeleteSingleCookieCallback>> m_deleteCookieCallbacks; 243 245 246 uint64_t m_nextSelectOptionElementCallbackID { 1 }; 247 HashMap<uint64_t, RefPtr<Inspector::AutomationBackendDispatcherHandler::SelectOptionElementCallback>> m_selectOptionElementCallbacks; 248 244 249 RunLoop::Timer<WebAutomationSession> m_loadTimer; 245 250 Vector<String> m_filesToSelectForFileUpload; -
trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.messages.in
r220314 r220740 29 29 DidComputeElementLayout(uint64_t callbackID, WebCore::IntRect rect, std::optional<WebCore::IntPoint> inViewCenterPoint, bool isObscured, String errorType) 30 30 31 DidSelectOptionElement(uint64_t callbackID, String errorType) 32 31 33 DidTakeScreenshot(uint64_t callbackID, WebKit::ShareableBitmap::Handle imageDataHandle, String errorType) 32 34 -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp
r220314 r220740 49 49 #include <WebCore/FrameView.h> 50 50 #include <WebCore/HTMLFrameElementBase.h> 51 #include <WebCore/HTMLOptGroupElement.h> 52 #include <WebCore/HTMLOptionElement.h> 53 #include <WebCore/HTMLSelectElement.h> 51 54 #include <WebCore/JSElement.h> 52 55 #include <WebCore/MainFrame.h> … … 504 507 } 505 508 509 static WebCore::Element* containerElementForElement(WebCore::Element& element) 510 { 511 // §13. Element State. 512 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-container. 513 if (is<WebCore::HTMLOptionElement>(element)) { 514 auto& optionElement = downcast<WebCore::HTMLOptionElement>(element); 515 #if ENABLE(DATALIST_ELEMENT) 516 if (auto* parentElement = optionElement.ownerDataListElement()) 517 return parentElement; 518 #endif 519 if (auto* parentElement = optionElement.ownerSelectElement()) 520 return parentElement; 521 522 return nullptr; 523 } 524 525 if (is<WebCore::HTMLOptGroupElement>(element)) { 526 if (auto* parentElement = downcast<WebCore::HTMLOptGroupElement>(element).ownerSelectElement()) 527 return parentElement; 528 529 return nullptr; 530 } 531 532 return &element; 533 } 534 506 535 void WebAutomationSessionProxy::computeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool useViewportCoordinates, uint64_t callbackID) 507 536 { … … 528 557 } 529 558 530 if (scrollIntoViewIfNeeded) { 559 auto* containerElement = containerElementForElement(*coreElement); 560 if (scrollIntoViewIfNeeded && containerElement) { 561 // §14.1 Element Click. Step 4. Scroll into view the element’s container. 562 // https://w3c.github.io/webdriver/webdriver-spec.html#element-click 563 containerElement->scrollIntoViewIfNeeded(false); 531 564 // FIXME: Wait in an implementation-specific way up to the session implicit wait timeout for the element to become in view. 532 coreElement->scrollIntoViewIfNeeded(false);533 565 } 534 566 … … 543 575 bool isObscured = false; 544 576 std::optional<WebCore::IntPoint> inViewCenter; 545 if (auto clientCenterPoint = elementInViewClientCenterPoint(*coreElement, isObscured)) { 546 inViewCenter = WebCore::IntPoint(coreFrameView->clientToDocumentPoint(clientCenterPoint.value())); 547 if (useViewportCoordinates) 548 inViewCenter = coreFrameView->contentsToRootView(inViewCenter.value()); 577 if (containerElement) { 578 if (auto clientCenterPoint = elementInViewClientCenterPoint(*containerElement, isObscured)) { 579 inViewCenter = WebCore::IntPoint(coreFrameView->clientToDocumentPoint(clientCenterPoint.value())); 580 if (useViewportCoordinates) 581 inViewCenter = coreFrameView->contentsToRootView(inViewCenter.value()); 582 } 549 583 } 550 584 551 585 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, rect, inViewCenter, isObscured, String()), 0); 586 } 587 588 void WebAutomationSessionProxy::selectOptionElement(uint64_t pageID, uint64_t frameID, String nodeHandle, uint64_t callbackID) 589 { 590 WebPage* page = WebProcess::singleton().webPage(pageID); 591 if (!page) { 592 String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound); 593 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, windowNotFoundErrorType), 0); 594 return; 595 } 596 597 WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame(); 598 if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) { 599 String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound); 600 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, frameNotFoundErrorType), 0); 601 return; 602 } 603 604 WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle); 605 if (!coreElement || (!is<WebCore::HTMLOptionElement>(coreElement) && !is<WebCore::HTMLOptGroupElement>(coreElement))) { 606 String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound); 607 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, nodeNotFoundErrorType), 0); 608 return; 609 } 610 611 String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable); 612 if (is<WebCore::HTMLOptGroupElement>(coreElement)) { 613 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, elementNotInteractableErrorType), 0); 614 return; 615 } 616 617 auto& optionElement = downcast<WebCore::HTMLOptionElement>(*coreElement); 618 auto* selectElement = optionElement.ownerSelectElement(); 619 if (!selectElement) { 620 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, elementNotInteractableErrorType), 0); 621 return; 622 } 623 624 if (selectElement->isDisabledFormControl() || optionElement.isDisabledFormControl()) { 625 String elementNotSelectableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotSelectable); 626 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, elementNotSelectableErrorType), 0); 627 return; 628 } 629 630 // FIXME: According to the spec we should fire mouse over, move and down events, then input and change, and finally mouse up and click. 631 // optionSelectedByUser() will fire input and change events if needed, but all other events should be fired manually here. 632 selectElement->optionSelectedByUser(optionElement.index(), true, selectElement->multiple()); 633 WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, { }), 0); 552 634 } 553 635 -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h
r219649 r220740 66 66 void focusFrame(uint64_t pageID, uint64_t frameID); 67 67 void computeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool useViewportCoordinates, uint64_t callbackID); 68 void selectOptionElement(uint64_t pageID, uint64_t frameID, String nodeHandle, uint64_t callbackID); 68 69 void takeScreenshot(uint64_t pageID, uint64_t callbackID); 69 70 void getCookiesForFrame(uint64_t pageID, uint64_t frameID, uint64_t callbackID); -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in
r203245 r220740 33 33 ComputeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool useViewportCoordinates, uint64_t callbackID) 34 34 35 SelectOptionElement(uint64_t pageID, uint64_t frameID, String nodeHandle, uint64_t callbackID) 36 35 37 TakeScreenshot(uint64_t pageID, uint64_t callbackID) 36 38
Note: See TracChangeset
for help on using the changeset viewer.