Revert set of a11y tree commits which cause crashes
It is not practical to revert each CL individually, so I have created this CL to bundle all the reverts together.da5d2ffb6d
4463d9a99f
2278ce249c
97778ef778
27716a230a
76b1d09313
faed33fe90
14c61afb23
962d5db870
b18739d115
4066dd6c1c
crrev.com/c/3964415 crrev.com/c/3970603 crrev.com/c/3974832 crrev.com/c/3976209 crrev.com/c/3971173 crrev.com/c/3972090 crrev.com/c/3970627 crrev.com/c/3970885 crrev.com/c/3976391 crrev.com/c/3978892 crrev.com/c/3975164 Revert "Move a11y to post-lifecycle steps (iteration #4)" This reverts commitda5d2ffb6d
. Revert "AXObjectCache is still dirty if tree updates are paused" This reverts commit4463d9a99f
. Revert "Remove extra call to ProcessDeferredAccessibilityEvents()" This reverts commit2278ce249c
. Revert "More robust handling of aria-activedescendant invalidations" This reverts commit97778ef778
. Revert "Avoid redundant calls to UpdateAXForAllDocuments()" This reverts commit27716a230a
. Revert "Restore popup guard" This reverts commit76b1d09313
. Revert "Use DCHECKs to prevent recursive calls in AXObjectCacheImpl" This reverts commitfaed33fe90
. Revert "Avoid raw pointer in WebAXObjectProxyList" This reverts commit14c61afb23
. Revert "Clean up load logic so that it's easier to understand" This reverts commit962d5db870
. Revert "Deflake web tests that create the root ax object" This reverts commitb18739d115
. Revert "Do not create orphaned AXObjects for whitespace text" This reverts commit4066dd6c1c
. Bug: 1376991 Change-Id: I9869417a9b9333fdf3a6b5796f94eabf9ac168e4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3990067 Commit-Queue: Joel Hockey <joelhockey@chromium.org> Owners-Override: Leo Zhang <googleo@google.com> Reviewed-by: Colin Kincaid <ckincaid@chromium.org> Cr-Commit-Position: refs/heads/main@{#1064718}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
003b9cc457
commit
3c7eb577ee
content
renderer
test
data
accessibility
web_test
third_party/blink
renderer
core
dom
frame
modules
accessibility
exported
web_tests
accessibility
@ -257,7 +257,6 @@ void RenderAccessibilityImpl::AccessibilityModeChanged(const ui::AXMode& mode) {
|
||||
if (settings) {
|
||||
if (mode.has_mode(ui::AXMode::kInlineTextBoxes)) {
|
||||
settings->SetInlineTextBoxAccessibilityEnabled(true);
|
||||
ax_context_->UpdateAXForAllDocuments();
|
||||
ComputeRoot().LoadInlineTextBoxes();
|
||||
} else {
|
||||
settings->SetInlineTextBoxAccessibilityEnabled(false);
|
||||
@ -372,7 +371,7 @@ void RenderAccessibilityImpl::HitTest(
|
||||
}
|
||||
|
||||
void RenderAccessibilityImpl::PerformAction(const ui::AXActionData& data) {
|
||||
WebDocument document = GetMainDocument();
|
||||
const WebDocument& document = GetMainDocument();
|
||||
if (document.IsNull())
|
||||
return;
|
||||
|
||||
@ -1148,11 +1147,16 @@ void RenderAccessibilityImpl::SendPendingAccessibilityEvents() {
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// Protect against lifecycle changes in the popup document, if any.
|
||||
// If no popup document, use the main document -- it's harmless to protect it
|
||||
// twice, and some document is needed because this cannot be done in an if
|
||||
// statement because it's scoped.
|
||||
WebDocument popup_document = GetPopupDocument();
|
||||
WebDocument popup_or_main_document =
|
||||
popup_document.IsNull() ? document : GetPopupDocument();
|
||||
std::unique_ptr<blink::WebDisallowTransitionScope> disallow2;
|
||||
if (!image_annotation_debugging_ && !popup_document.IsNull()) {
|
||||
disallow2 =
|
||||
std::make_unique<blink::WebDisallowTransitionScope>(&popup_document);
|
||||
if (!image_annotation_debugging_) {
|
||||
disallow = std::make_unique<blink::WebDisallowTransitionScope>(
|
||||
&popup_or_main_document);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -465,6 +465,170 @@ TEST_F(RenderAccessibilityImplTest, SendFullAccessibilityTreeOnReload) {
|
||||
EXPECT_EQ(6, CountAccessibilityNodesSentToBrowser());
|
||||
}
|
||||
|
||||
TEST_F(RenderAccessibilityImplTest, TestDeferred) {
|
||||
constexpr char html[] = R"HTML(
|
||||
<body>
|
||||
<div>
|
||||
a
|
||||
</div>
|
||||
</body>
|
||||
)HTML";
|
||||
LoadHTML(html);
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// We should have had load complete. Subsequent events are deferred unless
|
||||
// there is a user interaction.
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
// Simulate a page load to test deferred behavior.
|
||||
GetRenderAccessibilityImpl()->DidCommitProvisionalLoad(
|
||||
ui::PageTransition::PAGE_TRANSITION_LINK);
|
||||
ClearHandledUpdates();
|
||||
WebDocument document = GetMainFrame()->GetDocument();
|
||||
EXPECT_FALSE(document.IsNull());
|
||||
WebAXObject root_obj = WebAXObject::FromWebDocument(document);
|
||||
EXPECT_FALSE(root_obj.IsNull());
|
||||
|
||||
// No events should have been scheduled or sent.
|
||||
ExpectScheduleStatusNotWaiting();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
// Send a non-interactive event, it should be scheduled with a delay.
|
||||
GetRenderAccessibilityImpl()->HandleAXEvent(
|
||||
ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLocationChanged));
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
task_environment_.RunUntilIdle();
|
||||
// Ensure event is not sent as it is scheduled with a delay.
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
// Perform action, causing immediate event processing.
|
||||
ui::AXActionData action;
|
||||
action.action = ax::mojom::Action::kFocus;
|
||||
GetRenderAccessibilityImpl()->PerformAction(action);
|
||||
ScheduleSendPendingAccessibilityEvents();
|
||||
|
||||
// Once in immediate mode, stays in immediate mode until events are sent.
|
||||
GetRenderAccessibilityImpl()->HandleAXEvent(
|
||||
ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLocationChanged));
|
||||
ExpectScheduleStatusScheduledImmediate();
|
||||
ExpectScheduleModeProcessEventsImmediately();
|
||||
|
||||
// Once events have been sent, defer next batch.
|
||||
ScheduleSendPendingAccessibilityEvents();
|
||||
task_environment_.RunUntilIdle();
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
const std::vector<ax::mojom::Event> kNonInteractiveEvents = {
|
||||
ax::mojom::Event::kAriaAttributeChanged,
|
||||
ax::mojom::Event::kChildrenChanged,
|
||||
ax::mojom::Event::kDocumentTitleChanged,
|
||||
ax::mojom::Event::kExpandedChanged,
|
||||
ax::mojom::Event::kHide,
|
||||
ax::mojom::Event::kLayoutComplete,
|
||||
ax::mojom::Event::kLocationChanged,
|
||||
ax::mojom::Event::kMenuListValueChanged,
|
||||
ax::mojom::Event::kRowCollapsed,
|
||||
ax::mojom::Event::kRowCountChanged,
|
||||
ax::mojom::Event::kRowExpanded,
|
||||
ax::mojom::Event::kScrollPositionChanged,
|
||||
ax::mojom::Event::kScrolledToAnchor,
|
||||
ax::mojom::Event::kSelectedChildrenChanged,
|
||||
ax::mojom::Event::kShow,
|
||||
ax::mojom::Event::kTextChanged};
|
||||
|
||||
for (ax::mojom::Event event : kNonInteractiveEvents) {
|
||||
// Send an interactive event, it should be scheduled with a delay.
|
||||
GetRenderAccessibilityImpl()->HandleAXEvent(
|
||||
ui::AXEvent(root_obj.AxID(), event));
|
||||
ExpectScheduleModeDeferEvents();
|
||||
}
|
||||
|
||||
ScheduleSendPendingAccessibilityEvents();
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
|
||||
const std::vector<ax::mojom::Event> kInteractiveEvents = {
|
||||
ax::mojom::Event::kActiveDescendantChanged,
|
||||
ax::mojom::Event::kBlur,
|
||||
ax::mojom::Event::kCheckedStateChanged,
|
||||
ax::mojom::Event::kClicked,
|
||||
ax::mojom::Event::kDocumentSelectionChanged,
|
||||
ax::mojom::Event::kFocus,
|
||||
ax::mojom::Event::kHover,
|
||||
ax::mojom::Event::kLoadComplete,
|
||||
ax::mojom::Event::kValueChanged};
|
||||
|
||||
for (ax::mojom::Event event : kInteractiveEvents) {
|
||||
// Once events have been sent, defer next batch.
|
||||
task_environment_.RunUntilIdle();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
|
||||
// Send an interactive event, it should be scheduled with a delay.
|
||||
GetRenderAccessibilityImpl()->HandleAXEvent(
|
||||
ui::AXEvent(root_obj.AxID(), event));
|
||||
ExpectScheduleModeProcessEventsImmediately();
|
||||
ExpectScheduleStatusScheduledImmediate();
|
||||
|
||||
ScheduleSendPendingAccessibilityEvents();
|
||||
}
|
||||
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// Event has been sent, no longer waiting on ack.
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
}
|
||||
|
||||
TEST_F(RenderAccessibilityImplTest, TestChangesOnFocusModeAreImmediate) {
|
||||
LoadHTML(R"HTML(
|
||||
<body>
|
||||
<div id=a tabindex=0>
|
||||
a
|
||||
</div>
|
||||
<script>document.getElementById('a').focus();</script>
|
||||
</body>
|
||||
)HTML");
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// We should have had load complete. Subsequent events are deferred unless
|
||||
// there is a user interaction.
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
// Simulate a page load to test deferred behavior.
|
||||
GetRenderAccessibilityImpl()->DidCommitProvisionalLoad(
|
||||
ui::PageTransition::PAGE_TRANSITION_LINK);
|
||||
ClearHandledUpdates();
|
||||
WebDocument document = GetMainFrame()->GetDocument();
|
||||
EXPECT_FALSE(document.IsNull());
|
||||
WebAXObject root_obj = WebAXObject::FromWebDocument(document);
|
||||
EXPECT_FALSE(root_obj.IsNull());
|
||||
|
||||
WebAXObject html = root_obj.ChildAt(0);
|
||||
WebAXObject body = html.ChildAt(0);
|
||||
WebAXObject node_a = body.ChildAt(0);
|
||||
|
||||
// No events should have been scheduled or sent.
|
||||
ExpectScheduleStatusNotWaiting();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
|
||||
// Marking the focused object dirty causes changes to be sent immediately.
|
||||
GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a, false);
|
||||
ExpectScheduleStatusScheduledImmediate();
|
||||
ExpectScheduleModeProcessEventsImmediately();
|
||||
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// Event has been sent, no longer waiting on ack.
|
||||
ExpectScheduleStatusScheduledDeferred();
|
||||
ExpectScheduleModeDeferEvents();
|
||||
}
|
||||
|
||||
TEST_F(RenderAccessibilityImplTest, HideAccessibilityObject) {
|
||||
// Test RenderAccessibilityImpl and make sure it sends the
|
||||
// proper event to the browser when an object in the tree
|
||||
|
@ -1,5 +1,5 @@
|
||||
EVENT_OBJECT_HIDE on <div.a> role=ROLE_SYSTEM_GROUPING name="Heading" level=2
|
||||
EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL
|
||||
EVENT_OBJECT_SHOW on <div.b> role=ROLE_SYSTEM_GROUPING name="Banner"
|
||||
IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL new_text={'<obj>' start=1 end=2}
|
||||
IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL old_text={'<obj>' start=0 end=1}
|
||||
IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL new_text={'<obj>' start=0 end=1}
|
||||
IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL old_text={'<obj>' start=0 end=1}
|
@ -163,9 +163,7 @@ AccessibilityController::~AccessibilityController() {
|
||||
}
|
||||
|
||||
void AccessibilityController::Reset() {
|
||||
if (!IsInstalled())
|
||||
return;
|
||||
elements_->Clear();
|
||||
elements_.Clear();
|
||||
notification_callback_.Reset();
|
||||
log_accessibility_events_ = false;
|
||||
ax_context_.reset();
|
||||
@ -174,7 +172,7 @@ void AccessibilityController::Reset() {
|
||||
void AccessibilityController::Install(blink::WebLocalFrame* frame) {
|
||||
ax_context_ = std::make_unique<blink::WebAXContext>(frame->GetDocument(),
|
||||
ui::kAXModeComplete);
|
||||
elements_ = std::make_unique<WebAXObjectProxyList>(*ax_context_);
|
||||
elements_.SetAXContext(ax_context_.get());
|
||||
frame->View()->GetSettings()->SetInlineTextBoxAccessibilityEnabled(true);
|
||||
|
||||
AccessibilityControllerBindings::Install(weak_factory_.GetWeakPtr(), frame);
|
||||
@ -200,9 +198,6 @@ void AccessibilityController::PostNotification(
|
||||
const blink::WebAXObject& target,
|
||||
const std::string& notification_name,
|
||||
const std::vector<ui::AXEventIntent>& event_intents) {
|
||||
if (!IsInstalled())
|
||||
return;
|
||||
|
||||
v8::Isolate* isolate = blink::MainThreadIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
@ -218,7 +213,7 @@ void AccessibilityController::PostNotification(
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
// Call notification listeners on the element.
|
||||
v8::Local<v8::Object> element_handle = elements_->GetOrCreate(target);
|
||||
v8::Local<v8::Object> element_handle = elements_.GetOrCreate(target);
|
||||
if (element_handle.IsEmpty())
|
||||
return;
|
||||
|
||||
@ -259,7 +254,7 @@ void AccessibilityController::UnsetNotificationListener() {
|
||||
|
||||
v8::Local<v8::Object> AccessibilityController::FocusedElement() {
|
||||
blink::WebFrame* frame = web_view()->MainFrame();
|
||||
if (!frame || !IsInstalled())
|
||||
if (!frame)
|
||||
return v8::Local<v8::Object>();
|
||||
|
||||
// TODO(lukasza): Finish adding OOPIF support to the web tests harness.
|
||||
@ -272,14 +267,11 @@ v8::Local<v8::Object> AccessibilityController::FocusedElement() {
|
||||
frame->ToWebLocalFrame()->GetDocument());
|
||||
if (focused_element.IsNull())
|
||||
focused_element = GetAccessibilityObjectForMainFrame();
|
||||
return elements_->GetOrCreate(focused_element);
|
||||
return elements_.GetOrCreate(focused_element);
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> AccessibilityController::RootElement() {
|
||||
if (!IsInstalled())
|
||||
return v8::Local<v8::Object>();
|
||||
ax_context_->UpdateAXForAllDocuments();
|
||||
return elements_->GetOrCreate(GetAccessibilityObjectForMainFrame());
|
||||
return elements_.GetOrCreate(GetAccessibilityObjectForMainFrame());
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> AccessibilityController::AccessibleElementById(
|
||||
@ -307,7 +299,7 @@ AccessibilityController::FindAccessibleElementByIdRecursive(
|
||||
if (!node.IsNull() && node.IsElementNode()) {
|
||||
blink::WebElement element = node.To<blink::WebElement>();
|
||||
if (element.GetAttribute("id") == id)
|
||||
return elements_->GetOrCreate(obj);
|
||||
return elements_.GetOrCreate(obj);
|
||||
}
|
||||
|
||||
unsigned childCount = obj.ChildCount();
|
||||
|
@ -61,7 +61,6 @@ class AccessibilityController {
|
||||
v8::Local<v8::Object> RootElement();
|
||||
v8::Local<v8::Object> AccessibleElementById(const std::string& id);
|
||||
bool CanCallAOMEventListeners() const;
|
||||
bool IsInstalled() { return elements_ != nullptr; }
|
||||
|
||||
v8::Local<v8::Object> FindAccessibleElementByIdRecursive(
|
||||
const blink::WebAXObject&,
|
||||
@ -72,7 +71,7 @@ class AccessibilityController {
|
||||
// If true, will log all accessibility notifications.
|
||||
bool log_accessibility_events_;
|
||||
|
||||
std::unique_ptr<WebAXObjectProxyList> elements_;
|
||||
WebAXObjectProxyList elements_;
|
||||
|
||||
v8::Persistent<v8::Function> notification_callback_;
|
||||
|
||||
|
@ -1771,8 +1771,7 @@ bool RootWebAXObjectProxy::IsRoot() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
WebAXObjectProxyList::WebAXObjectProxyList(blink::WebAXContext& ax_context)
|
||||
: ax_context_(&ax_context) {}
|
||||
WebAXObjectProxyList::WebAXObjectProxyList() = default;
|
||||
|
||||
WebAXObjectProxyList::~WebAXObjectProxyList() {
|
||||
Clear();
|
||||
@ -1832,6 +1831,10 @@ v8::Local<v8::Object> WebAXObjectProxyList::GetOrCreate(
|
||||
return handle;
|
||||
}
|
||||
|
||||
void WebAXObjectProxyList::SetAXContext(blink::WebAXContext* ax_context) {
|
||||
ax_context_ = ax_context;
|
||||
}
|
||||
|
||||
blink::WebAXContext* WebAXObjectProxyList::GetAXContext() {
|
||||
return ax_context_;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ class WebAXObjectProxy : public gin::Wrappable<WebAXObjectProxy> {
|
||||
virtual ~Factory() {}
|
||||
virtual v8::Local<v8::Object> GetOrCreate(
|
||||
const blink::WebAXObject& object) = 0;
|
||||
virtual void SetAXContext(blink::WebAXContext* ax_context) = 0;
|
||||
virtual blink::WebAXContext* GetAXContext() = 0;
|
||||
};
|
||||
|
||||
@ -256,18 +257,30 @@ class RootWebAXObjectProxy : public WebAXObjectProxy {
|
||||
bool IsRoot() const override;
|
||||
};
|
||||
|
||||
// Provides simple lifetime management of the WebAXObjectProxy instances: all
|
||||
// WebAXObjectProxys ever created from the controller are stored in a list and
|
||||
// cleared explicitly.
|
||||
// TODO(aleventhal) Don't store ax_context_ as a non-const raw pointer.
|
||||
// Refactor as the following:
|
||||
// - The constructor takes a blink::WebAXContext& argument.
|
||||
// - ax_context_ is declared as 'blink::WebAXContext* const ax_context_;'
|
||||
// - Get rid of SetAXContext()
|
||||
// - std::unique_ptr<WebAXObjectProxyList> AccessibilityController::elements_;
|
||||
// - AccessibilityController::elements_ is populated from
|
||||
// AccessibilityController::Install().
|
||||
class WebAXObjectProxyList : public WebAXObjectProxy::Factory {
|
||||
public:
|
||||
explicit WebAXObjectProxyList(blink::WebAXContext&);
|
||||
WebAXObjectProxyList();
|
||||
~WebAXObjectProxyList() override;
|
||||
|
||||
void Clear();
|
||||
v8::Local<v8::Object> GetOrCreate(const blink::WebAXObject&) override;
|
||||
void SetAXContext(blink::WebAXContext* ax_context) override;
|
||||
blink::WebAXContext* GetAXContext() override;
|
||||
|
||||
private:
|
||||
std::vector<v8::Global<v8::Object>> elements_;
|
||||
blink::WebAXContext* const ax_context_;
|
||||
blink::WebAXContext* ax_context_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
19
third_party/blink/renderer/core/dom/document.cc
vendored
19
third_party/blink/renderer/core/dom/document.cc
vendored
@ -3466,13 +3466,20 @@ DocumentParser* Document::ImplicitOpen(
|
||||
}
|
||||
|
||||
void Document::DispatchHandleLoadStart() {
|
||||
if (AXObjectCache* cache = ExistingAXObjectCache())
|
||||
cache->HandleLoadStart(this);
|
||||
if (AXObjectCache* cache = ExistingAXObjectCache()) {
|
||||
// Don't fire load start for popup document.
|
||||
if (this == &AXObjectCacheOwner())
|
||||
cache->HandleLoadStart(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Document::DispatchHandleLoadComplete() {
|
||||
if (AXObjectCache* cache = ExistingAXObjectCache())
|
||||
cache->HandleLoadComplete(this);
|
||||
void Document::DispatchHandleLoadOrLayoutComplete() {
|
||||
if (AXObjectCache* cache = ExistingAXObjectCache()) {
|
||||
if (this == &AXObjectCacheOwner())
|
||||
cache->HandleLoadComplete(this);
|
||||
else
|
||||
cache->HandleLayoutComplete(this);
|
||||
}
|
||||
}
|
||||
|
||||
HTMLElement* Document::body() const {
|
||||
@ -3711,7 +3718,7 @@ void Document::ImplicitClose() {
|
||||
load_event_progress_ = kLoadEventCompleted;
|
||||
|
||||
if (GetFrame() && GetLayoutView()) {
|
||||
DispatchHandleLoadComplete();
|
||||
DispatchHandleLoadOrLayoutComplete();
|
||||
FontFaceSetDocument::DidLayout(*this);
|
||||
}
|
||||
|
||||
|
@ -1766,7 +1766,7 @@ class CORE_EXPORT Document : public ContainerNode,
|
||||
bool IsAccessibilityEnabled() const { return !ax_contexts_.empty(); }
|
||||
|
||||
void DispatchHandleLoadStart();
|
||||
void DispatchHandleLoadComplete();
|
||||
void DispatchHandleLoadOrLayoutComplete();
|
||||
|
||||
bool HaveRenderBlockingStylesheetsLoaded() const;
|
||||
bool HaveRenderBlockingResourcesLoaded() const;
|
||||
|
@ -128,6 +128,19 @@ bool DocumentLifecycle::CanAdvanceTo(LifecycleState next_state) const {
|
||||
return true;
|
||||
if (next_state == kStyleClean)
|
||||
return true;
|
||||
// InAccessibility only runs if there is an ExistingAXObjectCache.
|
||||
if (next_state == kInAccessibility)
|
||||
return true;
|
||||
if (next_state == kInCompositingInputsUpdate)
|
||||
return true;
|
||||
if (next_state == kInPrePaint)
|
||||
return true;
|
||||
break;
|
||||
case kInAccessibility:
|
||||
if (next_state == kAccessibilityClean)
|
||||
return true;
|
||||
break;
|
||||
case kAccessibilityClean:
|
||||
if (next_state == kInCompositingInputsUpdate)
|
||||
return true;
|
||||
if (next_state == kInPrePaint)
|
||||
@ -143,6 +156,8 @@ bool DocumentLifecycle::CanAdvanceTo(LifecycleState next_state) const {
|
||||
return true;
|
||||
if (next_state == kInPrePaint)
|
||||
return true;
|
||||
if (next_state == kInAccessibility)
|
||||
return true;
|
||||
break;
|
||||
case kInPrePaint:
|
||||
if (next_state == kPrePaintClean)
|
||||
@ -157,6 +172,8 @@ bool DocumentLifecycle::CanAdvanceTo(LifecycleState next_state) const {
|
||||
return true;
|
||||
if (next_state == kInPrePaint)
|
||||
return true;
|
||||
if (next_state == kInAccessibility)
|
||||
return true;
|
||||
break;
|
||||
case kInPaint:
|
||||
if (next_state == kPaintClean)
|
||||
@ -171,6 +188,8 @@ bool DocumentLifecycle::CanAdvanceTo(LifecycleState next_state) const {
|
||||
return true;
|
||||
if (next_state == kInPaint)
|
||||
return true;
|
||||
if (next_state == kInAccessibility)
|
||||
return true;
|
||||
break;
|
||||
case kStopping:
|
||||
return next_state == kStopped;
|
||||
@ -190,8 +209,9 @@ bool DocumentLifecycle::CanRewindTo(LifecycleState next_state) const {
|
||||
next_state == g_deprecated_transition_stack->To())
|
||||
return true;
|
||||
return state_ == kStyleClean || state_ == kAfterPerformLayout ||
|
||||
state_ == kLayoutClean || state_ == kCompositingInputsClean ||
|
||||
state_ == kPrePaintClean || state_ == kPaintClean;
|
||||
state_ == kLayoutClean || state_ == kAccessibilityClean ||
|
||||
state_ == kCompositingInputsClean || state_ == kPrePaintClean ||
|
||||
state_ == kPaintClean;
|
||||
}
|
||||
|
||||
#define DEBUG_STRING_CASE(StateName) \
|
||||
@ -209,6 +229,8 @@ static WTF::String StateAsDebugString(
|
||||
DEBUG_STRING_CASE(kInPerformLayout);
|
||||
DEBUG_STRING_CASE(kAfterPerformLayout);
|
||||
DEBUG_STRING_CASE(kLayoutClean);
|
||||
DEBUG_STRING_CASE(kInAccessibility);
|
||||
DEBUG_STRING_CASE(kAccessibilityClean);
|
||||
DEBUG_STRING_CASE(kInCompositingInputsUpdate);
|
||||
DEBUG_STRING_CASE(kCompositingInputsClean);
|
||||
DEBUG_STRING_CASE(kInPrePaint);
|
||||
|
@ -62,6 +62,11 @@ class CORE_EXPORT DocumentLifecycle {
|
||||
kAfterPerformLayout,
|
||||
kLayoutClean,
|
||||
|
||||
// In InAccessibility step, fire deferred accessibility events which
|
||||
// require layout to be in a clean state.
|
||||
kInAccessibility,
|
||||
kAccessibilityClean,
|
||||
|
||||
kInCompositingInputsUpdate,
|
||||
kCompositingInputsClean,
|
||||
|
||||
|
@ -2129,7 +2129,6 @@ const AtomicString& Element::computedRole() {
|
||||
DocumentUpdateReason::kJavaScript);
|
||||
}
|
||||
AXContext ax_context(document, ui::kAXModeBasic);
|
||||
ax_context.GetAXObjectCache().ProcessDeferredAccessibilityEvents(document);
|
||||
return ax_context.GetAXObjectCache().ComputedRoleForNode(this);
|
||||
}
|
||||
|
||||
@ -2143,7 +2142,6 @@ String Element::computedName() {
|
||||
DocumentUpdateReason::kJavaScript);
|
||||
}
|
||||
AXContext ax_context(document, ui::kAXModeBasic);
|
||||
ax_context.GetAXObjectCache().ProcessDeferredAccessibilityEvents(document);
|
||||
return ax_context.GetAXObjectCache().ComputedNameForNode(this);
|
||||
}
|
||||
|
||||
|
@ -2639,7 +2639,7 @@ void LocalFrame::DidResume() {
|
||||
|
||||
// TODO(yuzus): Figure out where these calls should really belong.
|
||||
GetDocument()->DispatchHandleLoadStart();
|
||||
GetDocument()->DispatchHandleLoadComplete();
|
||||
GetDocument()->DispatchHandleLoadOrLayoutComplete();
|
||||
}
|
||||
|
||||
void LocalFrame::MaybeLogAdClickNavigation() {
|
||||
|
@ -1041,7 +1041,6 @@ void LocalFrameView::RunPostLifecycleSteps() {
|
||||
base::AutoReset<bool> in_post_lifecycle_steps(&in_post_lifecycle_steps_,
|
||||
true);
|
||||
AllowThrottlingScope allow_throttling(*this);
|
||||
RunAccessibilitySteps();
|
||||
RunIntersectionObserverSteps();
|
||||
if (mobile_friendliness_checker_)
|
||||
mobile_friendliness_checker_->MaybeRecompute();
|
||||
@ -2254,6 +2253,7 @@ bool LocalFrameView::UpdateLifecyclePhases(
|
||||
|
||||
// Only the following target states are supported.
|
||||
DCHECK(target_state == DocumentLifecycle::kLayoutClean ||
|
||||
target_state == DocumentLifecycle::kAccessibilityClean ||
|
||||
target_state == DocumentLifecycle::kCompositingInputsClean ||
|
||||
target_state == DocumentLifecycle::kPrePaintClean ||
|
||||
target_state == DocumentLifecycle::kPaintClean);
|
||||
@ -2434,6 +2434,13 @@ void LocalFrameView::UpdateLifecyclePhasesInternal(
|
||||
DisallowLayoutInvalidationScope disallow_layout_invalidation(this);
|
||||
#endif
|
||||
|
||||
DCHECK_GE(target_state, DocumentLifecycle::kAccessibilityClean);
|
||||
run_more_lifecycle_phases = RunAccessibilityLifecyclePhase(target_state);
|
||||
DCHECK(ShouldThrottleRendering() || !ExistingAXObjectCache() ||
|
||||
Lifecycle().GetState() == DocumentLifecycle::kAccessibilityClean);
|
||||
if (!run_more_lifecycle_phases)
|
||||
return;
|
||||
|
||||
DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT_WITH_CATEGORIES(
|
||||
TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "SetLayerTreeId",
|
||||
inspector_set_layer_tree_id::Data, frame_.Get());
|
||||
@ -2842,8 +2849,10 @@ void LocalFrameView::RunPaintLifecyclePhase(PaintBenchmarkMode benchmark_mode) {
|
||||
GetPage()->Animator().ReportFrameAnimations(GetCompositorAnimationHost());
|
||||
}
|
||||
|
||||
void LocalFrameView::RunAccessibilitySteps() {
|
||||
TRACE_EVENT0("blink,benchmark", "LocalFrameView::RunAccessibilitySteps");
|
||||
bool LocalFrameView::RunAccessibilityLifecyclePhase(
|
||||
DocumentLifecycle::LifecycleState target_state) {
|
||||
TRACE_EVENT0("blink,benchmark",
|
||||
"LocalFrameView::RunAccessibilityLifecyclePhase");
|
||||
|
||||
SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
|
||||
LocalFrameUkmAggregator::kAccessibility);
|
||||
@ -2854,10 +2863,14 @@ void LocalFrameView::RunAccessibilitySteps() {
|
||||
|
||||
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
|
||||
if (AXObjectCache* cache = frame_view.ExistingAXObjectCache()) {
|
||||
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInAccessibility);
|
||||
cache->ProcessDeferredAccessibilityEvents(
|
||||
*frame_view.GetFrame().GetDocument());
|
||||
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kAccessibilityClean);
|
||||
}
|
||||
});
|
||||
|
||||
return target_state > DocumentLifecycle::kAccessibilityClean;
|
||||
}
|
||||
|
||||
void LocalFrameView::EnqueueScrollAnchoringAdjustment(
|
||||
|
@ -894,6 +894,8 @@ class CORE_EXPORT LocalFrameView final
|
||||
// earlier if we don't need to run future lifecycle phases.
|
||||
bool RunStyleAndLayoutLifecyclePhases(
|
||||
DocumentLifecycle::LifecycleState target_state);
|
||||
bool RunAccessibilityLifecyclePhase(
|
||||
DocumentLifecycle::LifecycleState target_state);
|
||||
bool RunCompositingInputsLifecyclePhase(
|
||||
DocumentLifecycle::LifecycleState target_state);
|
||||
bool RunPrePaintLifecyclePhase(
|
||||
@ -914,7 +916,6 @@ class CORE_EXPORT LocalFrameView final
|
||||
|
||||
DocumentLifecycle& Lifecycle() const;
|
||||
|
||||
void RunAccessibilitySteps();
|
||||
void RunIntersectionObserverSteps();
|
||||
void RenderThrottlingStatusChanged();
|
||||
|
||||
|
@ -110,16 +110,19 @@
|
||||
#include "ui/accessibility/mojom/ax_relative_bounds.mojom-blink.h"
|
||||
|
||||
// Prevent code that runs during the lifetime of the stack from altering the
|
||||
// document lifecycle, for the main document, and the popup document if present.
|
||||
// document lifecycle. Usually doc is the same as document_, but it can be
|
||||
// different when it is a popup document. Because it's harmless to test both
|
||||
// documents, even if they are the same, the scoped check is initialized for
|
||||
// both documents.
|
||||
// clang-format off
|
||||
#if DCHECK_IS_ON()
|
||||
#define SCOPED_DISALLOW_LIFECYCLE_TRANSITION() \
|
||||
DocumentLifecycle::DisallowTransitionScope scoped(document_->Lifecycle()); \
|
||||
DocumentLifecycle::DisallowTransitionScope scoped2( \
|
||||
popup_document_ ? popup_document_->Lifecycle() \
|
||||
: document_->Lifecycle());
|
||||
#define SCOPED_DISALLOW_LIFECYCLE_TRANSITION(document) \
|
||||
DocumentLifecycle::DisallowTransitionScope scoped1((document).Lifecycle()); \
|
||||
DocumentLifecycle::DisallowTransitionScope scoped2(document_->Lifecycle())
|
||||
#else
|
||||
#define SCOPED_DISALLOW_LIFECYCLE_TRANSITION()
|
||||
#define SCOPED_DISALLOW_LIFECYCLE_TRANSITION(document)
|
||||
#endif // DCHECK_IS_ON()
|
||||
// clang-format on
|
||||
|
||||
namespace blink {
|
||||
|
||||
@ -705,48 +708,54 @@ Node* AXObjectCacheImpl::FocusedElement() {
|
||||
return focused_node;
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::UpdateLayoutForAllDocuments() {
|
||||
UpdateLifecycleIfNeeded(GetDocument());
|
||||
if (Document* popup_document = GetPopupDocumentIfShowing())
|
||||
UpdateLifecycleIfNeeded(*popup_document);
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::UpdateLifecycleIfNeeded(Document& document) {
|
||||
DCHECK(document.defaultView());
|
||||
DCHECK(document.GetFrame());
|
||||
DCHECK(document.View());
|
||||
|
||||
// TODO(accessibility) This temporarily requires kPrePaintClean as
|
||||
// WebAXObject::UpdateLayout() did. Once a11y is moved to
|
||||
// PostRunLifecycleTasks, restore to only require kLayoutClean.
|
||||
// TODO(accessibility) Remove conditions and just update the lifecycle.
|
||||
if (document.NeedsLayoutTreeUpdate() || document.View()->NeedsLayout() ||
|
||||
document.Lifecycle().GetState() < DocumentLifecycle::kLayoutClean) {
|
||||
document.Lifecycle().GetState() < DocumentLifecycle::kPrePaintClean) {
|
||||
document.View()->UpdateAllLifecyclePhasesExceptPaint(
|
||||
DocumentUpdateReason::kAccessibility);
|
||||
}
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::UpdateAXForAllDocuments() {
|
||||
#if DCHECK_IS_ON()
|
||||
DCHECK(!IsFrozen())
|
||||
<< "Don't call UpdateAXForAllDocuments() here; layout and a11y are "
|
||||
"already clean at the start of serialization.";
|
||||
DCHECK(!updating_layout_and_ax_) << "Undesirable recursion.";
|
||||
base::AutoReset<bool> updating(&updating_layout_and_ax_, true);
|
||||
#endif
|
||||
UpdateLayoutForAllDocuments();
|
||||
|
||||
// First update the layout for the main and popup document.
|
||||
UpdateLifecycleIfNeeded(GetDocument());
|
||||
if (Document* popup_document = GetPopupDocumentIfShowing())
|
||||
UpdateLifecycleIfNeeded(*popup_document);
|
||||
|
||||
// Next flush all accessibility events and dirty objects, for both the main
|
||||
// and popup document.
|
||||
if (IsDirty())
|
||||
if (IsMainDocumentDirty())
|
||||
ProcessDeferredAccessibilityEvents(GetDocument());
|
||||
|
||||
if (IsPopupDocumentDirty())
|
||||
ProcessDeferredAccessibilityEvents(*GetPopupDocumentIfShowing());
|
||||
}
|
||||
|
||||
AXObject* AXObjectCacheImpl::GetOrCreateFocusedObjectFromNode(Node* node) {
|
||||
#if DCHECK_IS_ON()
|
||||
DCHECK(GetDocument().Lifecycle().GetState() >=
|
||||
DocumentLifecycle::kAfterPerformLayout);
|
||||
if (GetPopupDocumentIfShowing()) {
|
||||
DCHECK(GetPopupDocumentIfShowing()->Lifecycle().GetState() >=
|
||||
DocumentLifecycle::kAfterPerformLayout);
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
// TODO(chrishtr): refactor to use UpdateLayoutForAllDocuments().
|
||||
if (node->GetDocument() != GetDocument() &&
|
||||
node->GetDocument().Lifecycle().GetState() <
|
||||
DocumentLifecycle::kLayoutClean) {
|
||||
// Node is in a different, unclean document. This can occur in an open
|
||||
// popup. Ensure the popup document has a clean layout before trying to
|
||||
// create an AXObject from a node in it.
|
||||
if (node->GetDocument().View()) {
|
||||
node->GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
|
||||
DocumentUpdateReason::kAccessibility);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
AXObject* obj = GetOrCreate(node);
|
||||
if (!obj)
|
||||
@ -1558,27 +1567,6 @@ AXObject* AXObjectCacheImpl::CreateAndInit(LayoutObject* layout_object,
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!parent_if_known &&
|
||||
(layout_object->IsText() || layout_object->IsPseudoElement() || !node)) {
|
||||
// If the parent is not known, it means we are creating an AXObject at an
|
||||
// arbitrary place in the tree. Ensure its parent has it as a
|
||||
// child. an thus is connected to the root in both directions,
|
||||
// and is not an orphan.
|
||||
// This is accomplished by creating an AXObject for |layout_object|, by
|
||||
// first asking the parent to create its children, and then returning the
|
||||
// matching AXObject for |layout_object|.
|
||||
// This prevents situations where we attempt to serialize a node
|
||||
// and fail, because the parent does not reach it via its children.
|
||||
// It is only known to be an issue with AXObjects backed by layout, where a
|
||||
// change to layout has invalidated the inclusion of something in the tree.
|
||||
// For now, do this only for text and pseudo content, as it is a smaller
|
||||
// change, but consider doing it for more/all nodes in the future.
|
||||
DCHECK(!use_axid)
|
||||
<< "Cannot enforce an AXID when creating in the middle of the tree.";
|
||||
parent->UpdateChildrenIfNecessary();
|
||||
return Get(layout_object);
|
||||
}
|
||||
|
||||
AXObject* new_obj = CreateFromRenderer(layout_object);
|
||||
|
||||
DCHECK(new_obj) << "Could not create AXObject for " << layout_object;
|
||||
@ -1942,11 +1930,28 @@ void AXObjectCacheImpl::DeferTreeUpdateInternal(base::OnceClosure callback,
|
||||
return;
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// TODO(accessibility) Restore this check. Currently must be removed because a
|
||||
// loop in ProcessDeferredAccessibilityEvents() is allowed to queue deferred
|
||||
// ChildrenChanged() events and process them.
|
||||
// DCHECK(!tree_update_document->GetPage()->Animator().IsServicingAnimations()
|
||||
// ||
|
||||
// (tree_update_document->Lifecycle().GetState() <
|
||||
// DocumentLifecycle::kInAccessibility ||
|
||||
// tree_update_document->Lifecycle().StateAllowsDetach()))
|
||||
// << "DeferTreeUpdateInternal should only be outside of the lifecycle or
|
||||
// "
|
||||
// "before the accessibility state:"
|
||||
// << "\n* IsServicingAnimations: "
|
||||
// << tree_update_document->GetPage()->Animator().IsServicingAnimations()
|
||||
// << "\n* Lifecycle: " << tree_update_document->Lifecycle().ToString();
|
||||
#endif
|
||||
|
||||
queue.push_back(MakeGarbageCollected<TreeUpdateParams>(
|
||||
obj->GetNode(), obj->AXObjectID(), ComputeEventFrom(),
|
||||
active_event_from_action_, ActiveEventIntents(), std::move(callback)));
|
||||
|
||||
// These events are fired during RunPostLifecycleTasks(),
|
||||
// These events are fired during DocumentLifecycle::kInAccessibility,
|
||||
// ensure there is a document lifecycle update scheduled.
|
||||
ScheduleVisualUpdate(*tree_update_document);
|
||||
}
|
||||
@ -1977,11 +1982,30 @@ void AXObjectCacheImpl::DeferTreeUpdateInternal(base::OnceClosure callback,
|
||||
return;
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// TODO(accessibility) Consider re-adding. However, it conflicts with some
|
||||
// calls from HandleTextMarkerDataAdded(), which need to defer even when
|
||||
// already in clean layout. Removing this is not dangerous -- it helped ensure
|
||||
// that we weren't bothering to defer when layout is already clean. It's
|
||||
// actually ok if that's wrong here or there.
|
||||
// DCHECK(!tree_update_document.GetPage()->Animator().IsServicingAnimations()
|
||||
// ||
|
||||
// (tree_update_document.Lifecycle().GetState() <
|
||||
// DocumentLifecycle::kInAccessibility ||
|
||||
// tree_update_document.Lifecycle().StateAllowsDetach()))
|
||||
// << "DeferTreeUpdateInternal should only be outside of the lifecycle or
|
||||
// "
|
||||
// "before the accessibility state:"
|
||||
// << "\n* IsServicingAnimations: "
|
||||
// << tree_update_document.GetPage()->Animator().IsServicingAnimations()
|
||||
// << "\n* Lifecycle: " << tree_update_document.Lifecycle().ToString();
|
||||
#endif
|
||||
|
||||
queue.push_back(MakeGarbageCollected<TreeUpdateParams>(
|
||||
node, 0, ComputeEventFrom(), active_event_from_action_,
|
||||
ActiveEventIntents(), std::move(callback)));
|
||||
|
||||
// These events are fired during RunPostLifecycleTasks(),
|
||||
// These events are fired during DocumentLifecycle::kInAccessibility,
|
||||
// ensure there is a document lifecycle update scheduled.
|
||||
ScheduleVisualUpdate(tree_update_document);
|
||||
}
|
||||
@ -2061,7 +2085,7 @@ void AXObjectCacheImpl::UpdateReverseTextRelations(
|
||||
|
||||
void AXObjectCacheImpl::StyleChanged(const LayoutObject* layout_object) {
|
||||
DCHECK(layout_object);
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(layout_object->GetDocument());
|
||||
Node* node = GetClosestNodeForLayoutObject(layout_object);
|
||||
if (node)
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::StyleChangedWithCleanLayout, node);
|
||||
@ -2148,7 +2172,8 @@ void AXObjectCacheImpl::TextChangedWithCleanLayout(
|
||||
if (obj) {
|
||||
if (obj->RoleValue() == ax::mojom::blink::Role::kStaticText &&
|
||||
obj->LastKnownIsIncludedInTreeValue()) {
|
||||
if (InlineTextBoxAccessibilityEnabled()) {
|
||||
Settings* settings = GetSettings();
|
||||
if (settings && settings->GetInlineTextBoxAccessibilityEnabled()) {
|
||||
// Update inline text box children.
|
||||
ChildrenChangedWithCleanLayout(optional_node_for_relation_update, obj);
|
||||
return;
|
||||
@ -2202,19 +2227,13 @@ void AXObjectCacheImpl::DocumentTitleChanged() {
|
||||
|
||||
void AXObjectCacheImpl::UpdateCacheAfterNodeIsAttached(Node* node) {
|
||||
DCHECK(node);
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
Document* document = DynamicTo<Document>(node);
|
||||
if (document) {
|
||||
// A popup is being shown.
|
||||
DCHECK(*document != GetDocument());
|
||||
DCHECK(!popup_document_);
|
||||
popup_document_ = document;
|
||||
DCHECK(IsPopup(*document));
|
||||
// Fire children changed on the focused element that owns this popup.
|
||||
ChildrenChanged(GetDocument().FocusedElement());
|
||||
return;
|
||||
popup_document_ = document;
|
||||
}
|
||||
|
||||
DeferTreeUpdate(
|
||||
&AXObjectCacheImpl::UpdateCacheAfterNodeIsAttachedWithCleanLayout, node);
|
||||
}
|
||||
@ -2479,37 +2498,12 @@ void AXObjectCacheImpl::ChildrenChangedWithCleanLayout(Node* optional_node,
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::ProcessDeferredAccessibilityEvents(Document& document) {
|
||||
if (IsPopup(document)) {
|
||||
// Only process popup document together with main document.
|
||||
DCHECK_EQ(&document, GetPopupDocumentIfShowing());
|
||||
// Since a change occurred in the popup, processing of both documents will
|
||||
// be needed. A visual update on the main document will force this.
|
||||
ScheduleVisualUpdate(GetDocument());
|
||||
return;
|
||||
}
|
||||
ProcessDeferredAccessibilityEventsImpl(document);
|
||||
|
||||
DCHECK_EQ(document, GetDocument());
|
||||
|
||||
DCHECK(!processing_deferred_events_);
|
||||
|
||||
if (IsDirty()) {
|
||||
if (GetPopupDocumentIfShowing()) {
|
||||
UpdateLifecycleIfNeeded(*GetPopupDocumentIfShowing());
|
||||
ProcessDeferredAccessibilityEventsImpl(*GetPopupDocumentIfShowing());
|
||||
}
|
||||
ProcessDeferredAccessibilityEventsImpl(document);
|
||||
}
|
||||
|
||||
// Accessibility is now clean for both documents: AXObjects can be safely
|
||||
// traversed and AXObject's properties can be safely fetched.
|
||||
// TODO(accessibility) Now that both documents are always processed at the
|
||||
// same time, consider modifying the InspectorAccessibilityAgent so that only
|
||||
// the callback for the main document is needed.
|
||||
for (auto agent : agents_) {
|
||||
// Accessibility is now clean: AXObjects can be safely traversed and
|
||||
// AXObject's properties can be safely fetched.
|
||||
for (auto agent : agents_)
|
||||
agent->AXReadyCallback(document);
|
||||
if (GetPopupDocumentIfShowing())
|
||||
agent->AXReadyCallback(*GetPopupDocumentIfShowing());
|
||||
}
|
||||
|
||||
// TODO(chrishtr): Accessibility serializations should happen now, on the
|
||||
// condition that enough time has passed since the last serialization.
|
||||
@ -2519,6 +2513,16 @@ void AXObjectCacheImpl::ProcessDeferredAccessibilityEventsImpl(
|
||||
Document& document) {
|
||||
TRACE_EVENT0("accessibility", "ProcessDeferredAccessibilityEvents");
|
||||
|
||||
DCHECK(document.Lifecycle().GetState() >= DocumentLifecycle::kInAccessibility)
|
||||
<< "Deferred events should only be processed during the "
|
||||
"accessibility document lifecycle or later.";
|
||||
|
||||
// When tree updates are paused, IsDirty() will return false. In this
|
||||
// situation we should not return early because we would never trigger the
|
||||
// code that resumes the tree updates, inside ProcessCleanLayoutCallbacks.
|
||||
if (!IsDirty() && !tree_updates_paused_)
|
||||
return;
|
||||
|
||||
DCHECK(GetDocument().IsAccessibilityEnabled())
|
||||
<< "ProcessDeferredAccessibilityEvents should not perform work when "
|
||||
"accessibility is not enabled."
|
||||
@ -2531,8 +2535,6 @@ void AXObjectCacheImpl::ProcessDeferredAccessibilityEventsImpl(
|
||||
int loop_counter = 0;
|
||||
#endif
|
||||
|
||||
base::AutoReset<bool> processing(&processing_deferred_events_, true);
|
||||
|
||||
do {
|
||||
// Destroy and recreate any objects which are no longer valid, for example
|
||||
// they used AXNodeObject and now must be an AXLayoutObject, or vice-versa.
|
||||
@ -2581,6 +2583,8 @@ bool AXObjectCacheImpl::IsPopupDocumentDirty() const {
|
||||
}
|
||||
|
||||
bool AXObjectCacheImpl::IsDirty() const {
|
||||
if (tree_updates_paused_)
|
||||
return false;
|
||||
return IsMainDocumentDirty() || IsPopupDocumentDirty() ||
|
||||
relation_cache_->IsDirty();
|
||||
}
|
||||
@ -2607,7 +2611,14 @@ bool AXObjectCacheImpl::IsPopup(Document& document) const {
|
||||
<< "The popup document's owner should be in the main document.";
|
||||
Page* main_page = GetDocument().GetPage();
|
||||
DCHECK(main_page);
|
||||
DCHECK_EQ(&document, popup_document_);
|
||||
// TODO(accessibility) Verify that the main document's popup is |document|.
|
||||
// PagePopupController* popup_controller =
|
||||
// PagePopupController::From(*main_page);
|
||||
// DCHECK(popup_controller);
|
||||
// AXObject* popup_root_ax_object = popup_controller->RootAXObject();
|
||||
// DCHECK(popup_root_ax_object);
|
||||
// DCHECK_EQ(popup_root_ax_object->GetDocument(), &document)
|
||||
// << "There can be only one active popup document.";
|
||||
}
|
||||
#endif
|
||||
return is_popup;
|
||||
@ -2678,8 +2689,7 @@ void AXObjectCacheImpl::ProcessInvalidatedObjects(Document& document) {
|
||||
// TODO(accessibility) That may be the only example of this, in which case
|
||||
// it could be handled in RoleChangedWithCleanLayout(), and the cached
|
||||
// parent could be used.
|
||||
AXObject* new_object = CreateAndInit(
|
||||
node, AXObject::ComputeNonARIAParent(*this, node), retained_axid);
|
||||
AXObject* new_object = CreateAndInit(node, nullptr, retained_axid);
|
||||
if (new_object) {
|
||||
// Any owned objects need to reset their parent_ to point to the
|
||||
// new object.
|
||||
@ -2758,7 +2768,7 @@ void AXObjectCacheImpl::ProcessInvalidatedObjects(Document& document) {
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::ProcessCleanLayoutCallbacks(Document& document) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(document);
|
||||
|
||||
if (tree_updates_paused_) {
|
||||
ChildrenChangedWithCleanLayout(nullptr, GetOrCreate(&document));
|
||||
@ -2875,7 +2885,7 @@ void AXObjectCacheImpl::PostNotification(AXObject* object,
|
||||
// It's possible for FireAXEventImmediately to post another notification.
|
||||
// If we're still in the accessibility document lifecycle, fire these events
|
||||
// immediately rather than deferring them.
|
||||
if (processing_deferred_events_) {
|
||||
if (document.Lifecycle().GetState() == DocumentLifecycle::kInAccessibility) {
|
||||
FireAXEventImmediately(object, event_type, ComputeEventFrom(),
|
||||
active_event_from_action_, ActiveEventIntents());
|
||||
return;
|
||||
@ -2886,7 +2896,7 @@ void AXObjectCacheImpl::PostNotification(AXObject* object,
|
||||
object, event_type, ComputeEventFrom(), active_event_from_action_,
|
||||
ActiveEventIntents()));
|
||||
|
||||
// These events are fired during RunPostLifecycleTasks(),
|
||||
// These events are fired during DocumentLifecycle::kInAccessibility,
|
||||
// ensure there is a visual update scheduled.
|
||||
ScheduleVisualUpdate(document);
|
||||
}
|
||||
@ -2907,7 +2917,9 @@ void AXObjectCacheImpl::ScheduleVisualUpdate(Document& document) {
|
||||
return;
|
||||
|
||||
if (!frame_view->CanThrottleRendering() &&
|
||||
!document.GetPage()->Animator().IsServicingAnimations()) {
|
||||
(!document.GetPage()->Animator().IsServicingAnimations() ||
|
||||
document.Lifecycle().GetState() >=
|
||||
DocumentLifecycle::kInAccessibility)) {
|
||||
page->Animator().ScheduleVisualUpdate(document.GetFrame());
|
||||
}
|
||||
}
|
||||
@ -2918,7 +2930,8 @@ void AXObjectCacheImpl::FireTreeUpdatedEventImmediately(
|
||||
ax::mojom::blink::Action event_from_action,
|
||||
const BlinkAXEventIntentsSet& event_intents,
|
||||
base::OnceClosure callback) {
|
||||
DCHECK(processing_deferred_events_);
|
||||
DCHECK_GE(document.Lifecycle().GetState(),
|
||||
DocumentLifecycle::kInAccessibility);
|
||||
|
||||
base::AutoReset<ax::mojom::blink::EventFrom> event_from_resetter(
|
||||
&active_event_from_, event_from);
|
||||
@ -2935,6 +2948,9 @@ void AXObjectCacheImpl::FireAXEventImmediately(
|
||||
ax::mojom::blink::EventFrom event_from,
|
||||
ax::mojom::blink::Action event_from_action,
|
||||
const BlinkAXEventIntentsSet& event_intents) {
|
||||
DCHECK_GE(obj->GetDocument()->Lifecycle().GetState(),
|
||||
DocumentLifecycle::kInAccessibility);
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// Make sure none of the layout views are in the process of being laid out.
|
||||
// Notifications should only be sent after the layoutObject has finished
|
||||
@ -2945,7 +2961,7 @@ void AXObjectCacheImpl::FireAXEventImmediately(
|
||||
DCHECK(!layout_object->View()->GetLayoutState());
|
||||
}
|
||||
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(*obj->GetDocument());
|
||||
#endif // DCHECK_IS_ON()
|
||||
|
||||
if (event_type == ax::mojom::blink::Event::kChildrenChanged &&
|
||||
@ -3008,7 +3024,7 @@ void AXObjectCacheImpl::ListboxSelectedChildrenChanged(
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::ListboxActiveIndexChanged(HTMLSelectElement* select) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(select->GetDocument());
|
||||
|
||||
auto* ax_object = DynamicTo<AXListBox>(Get(select));
|
||||
if (!ax_object)
|
||||
@ -3057,7 +3073,7 @@ void AXObjectCacheImpl::HandleAriaExpandedChangeWithCleanLayout(Node* node) {
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
|
||||
DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
|
||||
if (AXObject* obj = GetOrCreate(node))
|
||||
@ -3081,31 +3097,15 @@ void AXObjectCacheImpl::HandleAriaPressedChangedWithCleanLayout(
|
||||
PostNotification(element, ax::mojom::blink::Event::kCheckedStateChanged);
|
||||
}
|
||||
|
||||
// In single selection containers, selection follows focus, so a selection
|
||||
// changed event must be fired. This ensures the AT is notified that the
|
||||
// selected state has changed, so that it does not read "unselected" as
|
||||
// the user navigates through the items. The event generator will handle
|
||||
// the correct events as long as the old and newly selected objects are marked
|
||||
// dirty.
|
||||
void AXObjectCacheImpl::HandleAriaSelectedChangedWithCleanLayout(Node* node) {
|
||||
DCHECK(node);
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
|
||||
DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
|
||||
AXObject* obj = Get(node);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
// Mark the previous selected item dirty if it was selected viaa "selection
|
||||
// follows focus".
|
||||
if (last_selected_from_active_descendant_)
|
||||
MarkElementDirtyWithCleanLayout(last_selected_from_active_descendant_);
|
||||
|
||||
// Mark the newly selected item dirty, and track it for use in the future.
|
||||
MarkAXObjectDirtyWithCleanLayout(obj);
|
||||
if (obj->IsSelectedFromFocus())
|
||||
last_selected_from_active_descendant_ = node;
|
||||
|
||||
PostNotification(obj, ax::mojom::Event::kCheckedStateChanged);
|
||||
|
||||
AXObject* listbox = obj->ParentObjectUnignored();
|
||||
@ -3139,6 +3139,15 @@ void AXObjectCacheImpl::HandleNodeGainedFocusWithCleanLayout(Node* node) {
|
||||
if (!node || !node->GetDocument().View())
|
||||
return;
|
||||
|
||||
// TODO(chrishtr): refactor to use UpdateLifecycleIfNeeded.
|
||||
if (node->GetDocument().NeedsLayoutTreeUpdateForNode(*node)) {
|
||||
// This should only occur when focus goes into a popup document. The main
|
||||
// document has an updated layout, but the popup does not.
|
||||
DCHECK_NE(document_, node->GetDocument());
|
||||
node->GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
|
||||
DocumentUpdateReason::kAccessibility);
|
||||
}
|
||||
|
||||
AXObject* obj = GetOrCreateFocusedObjectFromNode(node);
|
||||
if (!obj)
|
||||
return;
|
||||
@ -3252,7 +3261,7 @@ void AXObjectCacheImpl::HandleAriaHiddenChangedWithCleanLayout(Node* node) {
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
DCHECK(!node->GetDocument().NeedsLayoutTreeUpdateForNode(*node));
|
||||
|
||||
AXObject* obj = GetOrCreate(node);
|
||||
@ -3516,7 +3525,7 @@ void AXObjectCacheImpl::RemoveValidationMessageObjectWithCleanLayout(
|
||||
void AXObjectCacheImpl::HandleValidationMessageVisibilityChanged(
|
||||
const Node* form_control) {
|
||||
DCHECK(form_control);
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(form_control->GetDocument());
|
||||
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::
|
||||
HandleValidationMessageVisibilityChangedWithCleanLayout,
|
||||
@ -3697,13 +3706,6 @@ void AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayoutHelper(
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
// If the content is inside the popup, mark the owning element dirty.
|
||||
// TODO(aleventhal): not sure why this works, but now that we run a11y in
|
||||
// PostRunLifecycleTasks(), we need this, otherwise the pending updates in
|
||||
// the popup aren't processed.
|
||||
if (IsPopup(*obj->GetDocument()))
|
||||
MarkElementDirty(GetDocument().FocusedElement());
|
||||
|
||||
WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(
|
||||
obj->GetDocument()->AXObjectCacheOwner().GetFrame());
|
||||
if (webframe && webframe->Client()) {
|
||||
@ -3739,7 +3741,6 @@ void AXObjectCacheImpl::MarkAXSubtreeDirtyWithCleanLayout(AXObject* obj) {
|
||||
void AXObjectCacheImpl::MarkAXObjectDirty(AXObject* obj) {
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
base::OnceClosure callback =
|
||||
WTF::BindOnce(&AXObjectCacheImpl::MarkAXObjectDirtyWithCleanLayout,
|
||||
WrapWeakPersistent(this), WrapWeakPersistent(obj));
|
||||
@ -3821,7 +3822,7 @@ void AXObjectCacheImpl::HandleFocusedUIElementChanged(
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// The focus can be in a different document when a popup is open.
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(focused_doc);
|
||||
#endif // DCHECK_IS_ON()
|
||||
|
||||
if (focused_doc.GetPage() && focused_doc.GetPage()->InsidePortal())
|
||||
@ -4019,10 +4020,7 @@ void AXObjectCacheImpl::SerializeDirtyObjectsAndEvents(
|
||||
size_t num_remaining_objects_to_serialize =
|
||||
dirty_objects_.size() + kMaxExtraDirtyObjectsToSerialize;
|
||||
|
||||
DCHECK_GE(GetDocument().Lifecycle().GetState(),
|
||||
DocumentLifecycle::kLayoutClean);
|
||||
DCHECK(!popup_document_ || popup_document_->Lifecycle().GetState() >=
|
||||
DocumentLifecycle::kLayoutClean);
|
||||
UpdateLayoutForAllDocuments();
|
||||
|
||||
while (!dirty_objects_.empty() && --num_remaining_objects_to_serialize > 0) {
|
||||
AXDirtyObject* current_dirty_object = std::move(dirty_objects_.front());
|
||||
@ -4052,7 +4050,7 @@ void AXObjectCacheImpl::SerializeDirtyObjectsAndEvents(
|
||||
// ends up skipping it. That's probably a Blink bug if that happens, but
|
||||
// still we need to make sure we don't keep trying the same object over
|
||||
// again.
|
||||
if (already_serialized_ids.Contains(obj->AXObjectID()))
|
||||
if (!already_serialized_ids.insert(obj->AXObjectID()).is_new_entry)
|
||||
continue; // No insertion, was already present.
|
||||
|
||||
ui::AXTreeUpdate update;
|
||||
@ -4071,20 +4069,11 @@ void AXObjectCacheImpl::SerializeDirtyObjectsAndEvents(
|
||||
}
|
||||
|
||||
DCHECK_GT(update.nodes.size(), 0U);
|
||||
|
||||
for (auto& node : update.nodes) {
|
||||
DCHECK(node.id);
|
||||
already_serialized_ids.insert(node.id);
|
||||
}
|
||||
|
||||
DCHECK(already_serialized_ids.Contains(obj->AXObjectID()))
|
||||
<< "Did not serialize original node, so it was probably not included "
|
||||
"in its parent's children, and should never have been created in "
|
||||
"the first place: "
|
||||
<< obj->ToString(true)
|
||||
<< "\nParent: " << obj->ParentObjectIncludedInTree()->ToString(true)
|
||||
<< "\nIndex in parent: " << obj->IndexInParent();
|
||||
|
||||
updates.push_back(update);
|
||||
}
|
||||
|
||||
@ -4156,7 +4145,7 @@ void AXObjectCacheImpl::HandleEditableTextContentChanged(Node* node) {
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
|
||||
DeferTreeUpdate(
|
||||
&AXObjectCacheImpl::HandleEditableTextContentChangedWithCleanLayout,
|
||||
@ -4272,7 +4261,7 @@ void AXObjectCacheImpl::HandleUpdateActiveMenuOptionWithCleanLayout(
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::DidShowMenuListPopup(LayoutObject* menu_list) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(menu_list->GetDocument());
|
||||
|
||||
DCHECK(menu_list->GetNode());
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::DidShowMenuListPopupWithCleanLayout,
|
||||
@ -4291,7 +4280,7 @@ void AXObjectCacheImpl::DidShowMenuListPopupWithCleanLayout(Node* menu_list) {
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::DidHideMenuListPopup(LayoutObject* menu_list) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(menu_list->GetDocument());
|
||||
|
||||
DCHECK(menu_list->GetNode());
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::DidHideMenuListPopupWithCleanLayout,
|
||||
@ -4310,22 +4299,18 @@ void AXObjectCacheImpl::DidHideMenuListPopupWithCleanLayout(Node* menu_list) {
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::HandleLoadStart(Document* document) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
if (!IsPopup(*document)) {
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::EnsurePostNotification, document,
|
||||
ax::mojom::blink::Event::kLoadStart);
|
||||
}
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(*document);
|
||||
MarkAXObjectDirty(Get(document));
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::EnsurePostNotification, document,
|
||||
ax::mojom::blink::Event::kLoadStart);
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::HandleLoadComplete(Document* document) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(*document);
|
||||
|
||||
// Popups do not need to fire a load complete message.
|
||||
if (!IsPopup(*document)) {
|
||||
AddPermissionStatusListener();
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::HandleLoadCompleteWithCleanLayout,
|
||||
document);
|
||||
}
|
||||
AddPermissionStatusListener();
|
||||
DeferTreeUpdate(&AXObjectCacheImpl::HandleLoadCompleteWithCleanLayout,
|
||||
document);
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::HandleLoadCompleteWithCleanLayout(Node* document_node) {
|
||||
@ -4344,14 +4329,7 @@ void AXObjectCacheImpl::HandleLoadCompleteWithCleanLayout(Node* document_node) {
|
||||
}
|
||||
|
||||
void AXObjectCacheImpl::HandleLayoutComplete(Document* document) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
DCHECK(document);
|
||||
// Do not fire kLayoutComplete for popup document.
|
||||
if (document == GetPopupDocumentIfShowing())
|
||||
return;
|
||||
|
||||
// TODO(accessibility) What is the purpose of firing kLayoutComplete?
|
||||
// Do we even need this?
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(*document);
|
||||
if (document->Lifecycle().GetState() >=
|
||||
DocumentLifecycle::kAfterPerformLayout) {
|
||||
PostNotification(GetOrCreate(document),
|
||||
@ -4366,7 +4344,7 @@ void AXObjectCacheImpl::HandleScrolledToAnchor(const Node* anchor_node) {
|
||||
if (!anchor_node)
|
||||
return;
|
||||
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(anchor_node->GetDocument());
|
||||
|
||||
AXObject* obj = GetOrCreate(anchor_node->GetLayoutObject());
|
||||
if (!obj)
|
||||
@ -4399,7 +4377,7 @@ void AXObjectCacheImpl::SerializerClearedNode(AXID id) {
|
||||
|
||||
void AXObjectCacheImpl::HandleScrollPositionChanged(
|
||||
LocalFrameView* frame_view) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(*frame_view->GetFrame().GetDocument());
|
||||
|
||||
InvalidateBoundingBoxForFixedOrStickyPosition();
|
||||
MarkElementDirty(document_);
|
||||
@ -4409,7 +4387,7 @@ void AXObjectCacheImpl::HandleScrollPositionChanged(
|
||||
|
||||
void AXObjectCacheImpl::HandleScrollPositionChanged(
|
||||
LayoutObject* layout_object) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(layout_object->GetDocument());
|
||||
InvalidateBoundingBoxForFixedOrStickyPosition();
|
||||
Node* node = GetClosestNodeForLayoutObject(layout_object);
|
||||
if (node) {
|
||||
@ -4420,7 +4398,7 @@ void AXObjectCacheImpl::HandleScrollPositionChanged(
|
||||
}
|
||||
|
||||
const AtomicString& AXObjectCacheImpl::ComputedRoleForNode(Node* node) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
|
||||
AXObject* obj = GetOrCreate(node);
|
||||
if (!obj)
|
||||
@ -4429,7 +4407,7 @@ const AtomicString& AXObjectCacheImpl::ComputedRoleForNode(Node* node) {
|
||||
}
|
||||
|
||||
String AXObjectCacheImpl::ComputedNameForNode(Node* node) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(node->GetDocument());
|
||||
AXObject* obj = GetOrCreate(node);
|
||||
if (!obj)
|
||||
return "";
|
||||
@ -4455,7 +4433,7 @@ void AXObjectCacheImpl::OnTouchAccessibilityHover(const gfx::Point& location) {
|
||||
void AXObjectCacheImpl::SetCanvasObjectBounds(HTMLCanvasElement* canvas,
|
||||
Element* element,
|
||||
const LayoutRect& rect) {
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION();
|
||||
SCOPED_DISALLOW_LIFECYCLE_TRANSITION(element->GetDocument());
|
||||
|
||||
AXObject* obj = GetOrCreate(element);
|
||||
if (!obj)
|
||||
@ -4533,7 +4511,6 @@ void AXObjectCacheImpl::Trace(Visitor* visitor) const {
|
||||
visitor->Trace(agents_);
|
||||
visitor->Trace(document_);
|
||||
visitor->Trace(popup_document_);
|
||||
visitor->Trace(last_selected_from_active_descendant_);
|
||||
visitor->Trace(accessible_node_mapping_);
|
||||
visitor->Trace(layout_object_mapping_);
|
||||
visitor->Trace(node_object_mapping_);
|
||||
|
@ -558,6 +558,7 @@ class MODULES_EXPORT AXObjectCacheImpl
|
||||
mojo::Remote<mojom::blink::RenderAccessibilityHost>&
|
||||
GetOrCreateRemoteRenderAccessibilityHost();
|
||||
void ProcessDeferredAccessibilityEventsImpl(Document&);
|
||||
void UpdateLayoutForAllDocuments();
|
||||
void UpdateLifecycleIfNeeded(Document& document);
|
||||
|
||||
bool IsMainDocumentDirty() const;
|
||||
@ -663,17 +664,8 @@ class MODULES_EXPORT AXObjectCacheImpl
|
||||
// AriaModalPrunesAXTree setting enabled, such as Mac.
|
||||
WeakMember<AXObject> active_aria_modal_dialog_;
|
||||
|
||||
// If non-null, this is the node that the current aria-activedescendant caused
|
||||
// to have the selected state.
|
||||
WeakMember<Node> last_selected_from_active_descendant_;
|
||||
|
||||
std::unique_ptr<AXRelationCache> relation_cache_;
|
||||
|
||||
bool processing_deferred_events_ = false;
|
||||
#if DCHECK_IS_ON()
|
||||
bool updating_layout_and_ax_ = false;
|
||||
#endif
|
||||
|
||||
// Verified when finalizing.
|
||||
bool has_been_disposed_ = false;
|
||||
|
||||
|
@ -113,6 +113,7 @@ TEST_F(AccessibilityTest, PauseUpdatesAfterMaxNumberQueued) {
|
||||
ax_object_cache->DeferTreeUpdate(
|
||||
&AXObjectCacheImpl::ChildrenChangedWithCleanLayout, ax_obj);
|
||||
}
|
||||
document.Lifecycle().AdvanceTo(DocumentLifecycle::kInAccessibility);
|
||||
ax_object_cache->ProcessCleanLayoutCallbacks(document);
|
||||
|
||||
ASSERT_EQ(0u, MockAXObject::num_children_changed_calls_);
|
||||
|
@ -25,7 +25,8 @@ void AccessibilityTest::SetUp() {
|
||||
|
||||
AXObjectCacheImpl& AccessibilityTest::GetAXObjectCache() const {
|
||||
DCHECK(GetDocument().View());
|
||||
GetDocument().View()->UpdateAllLifecyclePhasesForTest();
|
||||
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
|
||||
DocumentUpdateReason::kAccessibility);
|
||||
auto* ax_object_cache =
|
||||
To<AXObjectCacheImpl>(GetDocument().ExistingAXObjectCache());
|
||||
DCHECK(ax_object_cache);
|
||||
|
@ -1317,6 +1317,7 @@ WebAXObject WebAXObject::FromWebDocument(const WebDocument& web_document) {
|
||||
const Document* document = web_document.ConstUnwrap<Document>();
|
||||
auto* cache = To<AXObjectCacheImpl>(document->ExistingAXObjectCache());
|
||||
DCHECK(cache);
|
||||
cache->UpdateAXForAllDocuments();
|
||||
return WebAXObject(cache->GetOrCreate(document));
|
||||
}
|
||||
|
||||
|
@ -139,8 +139,8 @@ async_test((t) => {
|
||||
]);
|
||||
// SelectedTextChanged at 10.
|
||||
expectedSelectedTextChangedIntents.push([
|
||||
'AXEventIntent(delete,deleteContentBackward,none,none)',
|
||||
'AXEventIntent(setSelection,none,character,forward)',
|
||||
'AXEventIntent(delete,deleteContentBackward,none,none)',
|
||||
]);
|
||||
eventSender.keyDown('Backspace', []);
|
||||
}, 'Ensures that moving the cursor in a contentEditable sends a selected text change notification, and typing in a contentEditable sends both a value changed and selected text changed notification.');
|
||||
|
Reference in New Issue
Block a user