blob: 98dc2951c76df9d7db919b1b4005ee7e4d49728d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_DISPLAY_LOCK_CONTEXT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_DISPLAY_LOCK_CONTEXT_H_
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_budget.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
namespace blink {
class DisplayLockSuspendedHandle;
class Element;
class DisplayLockOptions;
class DisplayLockScopedLogger;
class TaskHandle;
class CORE_EXPORT DisplayLockContext final
: public ScriptWrappable,
public ActiveScriptWrappable<DisplayLockContext>,
public ContextLifecycleObserver,
public LocalFrameView::LifecycleNotificationObserver {
DEFINE_WRAPPERTYPEINFO();
USING_GARBAGE_COLLECTED_MIXIN(DisplayLockContext);
USING_PRE_FINALIZER(DisplayLockContext, Dispose);
public:
// Determines what type of budget to use. This can be overridden via
// DisplayLockContext::SetBudgetType().
// - kDoNotYield:
// This will effectively do all of the lifecycle phases when we're
// committing. That is, this is the "no budget" option.
// - kStrictYieldBetweenLifecyclePhases:
// This type always yields between each lifecycle phase, even if that
// phase was quick (note that it still skips phases that don't need any
// updates).
// - kYieldBetweenLifecyclePhases:
// This type will only yield between lifecycle phases (not in the middle
// of one). However, if there is sufficient time left (TODO(vmpstr):
// define this), then it will continue on to the next lifecycle phase.
enum class BudgetType {
kDoNotYield,
kStrictYieldBetweenLifecyclePhases,
kYieldBetweenLifecyclePhases,
kDefault = kYieldBetweenLifecyclePhases
};
// The current state of the lock. Note that the order of these matters.
enum State {
kLocked,
kUpdating,
kCommitting,
kUnlocked,
};
// The type of style that was blocked by this display lock.
enum StyleType {
kStyleUpdateNotRequired,
kStyleUpdateSelf,
kStyleUpdateChildren,
kStyleUpdateDescendants
};
// See GetScopedForcedUpdate() for description.
class CORE_EXPORT ScopedForcedUpdate {
DISALLOW_NEW();
public:
ScopedForcedUpdate(ScopedForcedUpdate&&);
~ScopedForcedUpdate();
private:
friend class DisplayLockContext;
ScopedForcedUpdate(DisplayLockContext*);
UntracedMember<DisplayLockContext> context_ = nullptr;
};
DisplayLockContext(Element*, ExecutionContext*);
~DisplayLockContext() override;
// GC functions.
void Trace(blink::Visitor*) override;
void Dispose();
// ContextLifecycleObserver overrides.
void ContextDestroyed(ExecutionContext*) override;
// ActiveScriptWrappable overrides. This keeps the context alive as long as we
// have an element and we're not unlocked.
bool HasPendingActivity() const final;
// JavaScript interface implementation. See display_lock_context.idl for
// description.
ScriptPromise acquire(ScriptState*, DisplayLockOptions*);
ScriptPromise update(ScriptState*);
ScriptPromise commit(ScriptState*);
ScriptPromise updateAndCommit(ScriptState*);
enum LifecycleTarget { kSelf, kChildren };
// Lifecycle observation / state functions.
bool ShouldStyle(LifecycleTarget) const;
void DidStyle(LifecycleTarget);
bool ShouldLayout(LifecycleTarget) const;
void DidLayout(LifecycleTarget);
bool ShouldPrePaint() const;
void DidPrePaint();
bool ShouldPaint() const;
void DidPaint();
// Returns true if the last style recalc traversal was blocked at this
// element, either for itself, its children or its descendants.
bool StyleTraversalWasBlocked() {
return blocked_style_traversal_type_ != kStyleUpdateNotRequired;
}
// Returns true if the contents of the associated element should be visible
// from and activatable by find-in-page, tab order, anchor links, etc.
bool IsActivatable() const;
// Trigger commit because of activation from tab order, url fragment,
// find-in-page, etc.
void CommitForActivation();
bool ShouldCommitForActivation() const;
// Returns true if this lock is locked. Note from the outside perspective, the
// lock is locked any time the state is not kUnlocked or kCommitting.
bool IsLocked() const { return state_ != kUnlocked && state_ != kCommitting; }
// Called when the layout tree is attached. This is used to verify
// containment.
void DidAttachLayoutTree();
// Returns a ScopedForcedUpdate object which for the duration of its lifetime
// will allow updates to happen on this element's subtree. For the element
// itself, the frame rect will still be the same as at the time the lock was
// acquired. Only one ScopedForcedUpdate can be retrieved from the same
// context at a time.
ScopedForcedUpdate GetScopedForcedUpdate();
// This is called when the element with which this context is associated is
// moved to a new document. Used to listen to the lifecycle update from the
// right document's view.
void DidMoveToNewDocument(Document& old_document);
void AddToWhitespaceReattachSet(Element& element);
// LifecycleNotificationObserver overrides.
void WillStartLifecycleUpdate(const LocalFrameView&) override;
void DidFinishLifecycleUpdate(const LocalFrameView&) override;
// Inform the display lock that it prevented a style change. This is used to
// invalidate style when we need to update it in the future.
void NotifyStyleRecalcWasBlocked(StyleType type) {
blocked_style_traversal_type_ =
std::max(blocked_style_traversal_type_, type);
}
// Notify this element will be disconnected.
void NotifyWillDisconnect();
void SetNeedsPrePaintSubtreeWalk(
bool needs_effective_allowed_touch_action_update) {
needs_effective_allowed_touch_action_update_ =
needs_effective_allowed_touch_action_update;
needs_prepaint_subtree_walk_ = true;
}
LayoutUnit GetLockedContentLogicalWidth() const {
return is_horizontal_writing_mode_ ? locked_content_logical_size_->Width()
: locked_content_logical_size_->Height();
}
LayoutUnit GetLockedContentLogicalHeight() const {
return is_horizontal_writing_mode_ ? locked_content_logical_size_->Height()
: locked_content_logical_size_->Width();
}
private:
friend class DisplayLockContextTest;
friend class DisplayLockBudgetTest;
friend class DisplayLockSuspendedHandle;
friend class DisplayLockBudget;
class StateChangeHelper {
DISALLOW_NEW();
public:
explicit StateChangeHelper(DisplayLockContext*);
operator State() const { return state_; }
StateChangeHelper& operator=(State);
void UpdateActivationBlockingCount(bool old_activatable,
bool new_activatable);
private:
State state_ = kUnlocked;
UntracedMember<DisplayLockContext> context_;
};
// Initiate a commit.
void StartCommit();
// Initiate an update.
void StartUpdateIfNeeded();
// Marks ancestors of elements in |whitespace_reattach_set_| with
// ChildNeedsReattachLayoutTree and clears the set.
void MarkElementsForWhitespaceReattachment();
// The following functions propagate dirty bits from the locked element up to
// the ancestors in order to be reached, and update dirty bits for the element
// as well if needed. They return true if the element or its subtree were
// dirty, and false otherwise.
bool MarkForStyleRecalcIfNeeded();
bool MarkAncestorsForLayoutIfNeeded();
bool MarkAncestorsForPrePaintIfNeeded();
bool MarkPaintLayerNeedsRepaint();
bool IsElementDirtyForStyleRecalc() const;
bool IsElementDirtyForLayout() const;
bool IsElementDirtyForPrePaint() const;
// When ScopedForcedUpdate is destroyed, it calls this function. See
// GetScopedForcedUpdate() for more information.
void NotifyForcedUpdateScopeEnded();
// Creates a new update budget based on the BudgetType::kDefault enum. In
// other words, it will create a budget of that type.
// TODO(vmpstr): In tests, we will probably switch the value to test other
// budgets. As well, this makes it easier to change the budget right in the
// enum definitions.
std::unique_ptr<DisplayLockBudget> CreateNewBudget();
// Helper to schedule an animation to delay lifecycle updates for the next
// frame.
void ScheduleAnimation();
// Timeout implementation. When the lock is acquired, we kick off a timeout
// task that will trigger a commit (which can be canceled by other calls to
// schedule or by a call to commit). Note that calling RescheduleTimeoutTask()
// will cancel any previously scheduled task.
void RescheduleTimeoutTask(double delay);
void CancelTimeoutTask();
void TriggerTimeout();
// Helper functions to resolve the update/commit promises.
enum ResolverState { kResolve, kReject, kDetach };
void FinishUpdateResolver(ResolverState, const char* reject_reason = nullptr);
void FinishCommitResolver(ResolverState, const char* reject_reason = nullptr);
void FinishAcquireResolver(ResolverState,
const char* reject_reason = nullptr);
void FinishResolver(Member<ScriptPromiseResolver>*,
ResolverState,
const char* reject_reason);
// Returns true if the element supports display locking. Note that this can
// only be called if the style is clean. It checks the layout object if it
// exists. Otherwise, falls back to checking computed style.
bool ElementSupportsDisplayLocking() const;
// Returns true if the element is connected to a document that has a view.
// If we're not connected, or if we're connected but the document doesn't
// have a view (e.g. templates) we shouldn't do style calculations etc and
// when acquiring this lock should immediately resolve the acquire promise.
bool ConnectedToView() const;
std::unique_ptr<DisplayLockBudget> update_budget_;
Member<ScriptPromiseResolver> commit_resolver_;
Member<ScriptPromiseResolver> update_resolver_;
Member<ScriptPromiseResolver> acquire_resolver_;
WeakMember<Element> element_;
WeakMember<Document> document_;
// See StyleEngine's |whitespace_reattach_set_|.
// Set of elements that had at least one rendered children removed
// since its last lifecycle update. For such elements that are located
// in a locked subtree, we save it here instead of the global set in
// StyleEngine because we don't want to accidentally mark elements
// in a locked subtree for layout tree reattachment before we did
// style recalc on them.
HeapHashSet<Member<Element>> whitespace_reattach_set_;
StateChangeHelper state_;
base::Optional<LayoutSize> locked_content_logical_size_;
bool update_forced_ = false;
bool activatable_ = false;
bool is_locked_after_connect_ = false;
StyleType blocked_style_traversal_type_ = kStyleUpdateNotRequired;
bool needs_effective_allowed_touch_action_update_ = false;
bool needs_prepaint_subtree_walk_ = false;
bool is_horizontal_writing_mode_ = true;
TaskHandle timeout_task_handle_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_DISPLAY_LOCK_CONTEXT_H_