// Copyright 2013 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 CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_ANDROID_H_
#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_ANDROID_H_

#include <stdint.h>

#include "base/android/scoped_java_ref.h"
#include "base/macros.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/android/content_view_core_impl.h"

namespace content {

namespace aria_strings {
  extern const char kAriaLivePolite[];
  extern const char kAriaLiveAssertive[];
}

// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content.browser.accessibility
enum ScrollDirection {
  FORWARD,
  BACKWARD,
  UP,
  DOWN,
  LEFT,
  RIGHT
};

// From android.view.accessibility.AccessibilityNodeInfo in Java:
enum AndroidMovementGranularity {
  ANDROID_ACCESSIBILITY_NODE_INFO_MOVEMENT_GRANULARITY_CHARACTER = 1,
  ANDROID_ACCESSIBILITY_NODE_INFO_MOVEMENT_GRANULARITY_WORD = 2,
  ANDROID_ACCESSIBILITY_NODE_INFO_MOVEMENT_GRANULARITY_LINE = 4
};

// From android.view.accessibility.AccessibilityEvent in Java:
enum {
  ANDROID_ACCESSIBILITY_EVENT_TEXT_CHANGED = 16,
  ANDROID_ACCESSIBILITY_EVENT_TEXT_SELECTION_CHANGED = 8192,
  ANDROID_ACCESSIBILITY_EVENT_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072
};

class BrowserAccessibilityAndroid;

class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
    : public BrowserAccessibilityManager {
 public:
  BrowserAccessibilityManagerAndroid(
      base::android::ScopedJavaLocalRef<jobject> content_view_core,
      const ui::AXTreeUpdate& initial_tree,
      BrowserAccessibilityDelegate* delegate,
      BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());

  ~BrowserAccessibilityManagerAndroid() override;

  static ui::AXTreeUpdate GetEmptyDocument();

  void SetContentViewCore(
      base::android::ScopedJavaLocalRef<jobject> content_view_core);

  // By default, the tree is pruned for a better screen reading experience,
  // including:
  //   * If the node has only static text children
  //   * If the node is focusable and has no focusable children
  //   * If the node is a heading
  // This can be turned off to generate a tree that more accurately reflects
  // the DOM and includes style changes within these nodes.
  void set_prune_tree_for_screen_reader(bool prune) {
    prune_tree_for_screen_reader_ = prune;
  }
  bool prune_tree_for_screen_reader() { return prune_tree_for_screen_reader_; }

  // Implementation of BrowserAccessibilityManager.
  void NotifyAccessibilityEvent(ui::AXEvent event_type,
                                BrowserAccessibility* node) override;
  void OnLocationChanges(
      const std::vector<AccessibilityHostMsg_LocationChangeParams>& params)
          override;

  // --------------------------------------------------------------------------
  // Methods called from Java via JNI
  // --------------------------------------------------------------------------

  // Tree methods.
  jint GetRootId(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
  jboolean IsNodeValid(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj,
                       jint id);
  void HitTest(JNIEnv* env,
               const base::android::JavaParamRef<jobject>& obj,
               jint x,
               jint y);

  // Methods to get information about a specific node.
  jboolean IsEditableText(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& obj,
                          jint id);
  jint GetEditableTextSelectionStart(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      jint id);
  jint GetEditableTextSelectionEnd(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      jint id);

  // Populate Java accessibility data structures with info about a node.
  jboolean PopulateAccessibilityNodeInfo(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      const base::android::JavaParamRef<jobject>& info,
      jint id);
  jboolean PopulateAccessibilityEvent(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      const base::android::JavaParamRef<jobject>& event,
      jint id,
      jint event_type);

  // Perform actions.
  void Click(JNIEnv* env,
             const base::android::JavaParamRef<jobject>& obj,
             jint id);
  void Focus(JNIEnv* env,
             const base::android::JavaParamRef<jobject>& obj,
             jint id);
  void Blur(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
  void ScrollToMakeNodeVisible(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj,
                               jint id);
  void SetTextFieldValue(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& obj,
                         jint id,
                         const base::android::JavaParamRef<jstring>& value);
  void SetSelection(JNIEnv* env,
                    const base::android::JavaParamRef<jobject>& obj,
                    jint id,
                    jint start,
                    jint end);
  jboolean AdjustSlider(JNIEnv* env,
                        const base::android::JavaParamRef<jobject>& obj,
                        jint id,
                        jboolean increment);

  // Return the id of the next node in tree order in the direction given by
  // |forwards|, starting with |start_id|, that matches |element_type|,
  // where |element_type| is a special uppercase string from TalkBack or
  // BrailleBack indicating general categories of web content like
  // "SECTION" or "CONTROL".  Return 0 if not found.
  jint FindElementType(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj,
                       jint start_id,
                       const base::android::JavaParamRef<jstring>& element_type,
                       jboolean forwards);

  // Respond to a ACTION_[NEXT/PREVIOUS]_AT_MOVEMENT_GRANULARITY action
  // and move the cursor/selection within the given node id. We keep track
  // of our own selection in BrowserAccessibilityManager.java for static
  // text, but if this is an editable text node, updates the selected text
  // in Blink, too, and either way calls
  // Java_BrowserAccessibilityManager_finishGranularityMove with the
  // result.
  jboolean NextAtGranularity(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj,
                             jint granularity,
                             jboolean extend_selection,
                             jint id,
                             jint cursor_index);
  jboolean PreviousAtGranularity(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      jint granularity,
      jboolean extend_selection,
      jint id,
      jint cursor_index);

  // Helper functions to compute the next start and end index when moving
  // forwards or backwards by character, word, or line. This part is
  // unit-tested; the Java interfaces above are just wrappers. Both of these
  // take a single cursor index as input and return the boundaries surrounding
  // the next word or line. If moving by character, the output start and
  // end index will be the same.
  bool NextAtGranularity(int32_t granularity,
                         int cursor_index,
                         BrowserAccessibilityAndroid* node,
                         int32_t* start_index,
                         int32_t* end_index);
  bool PreviousAtGranularity(int32_t granularity,
                             int cursor_index,
                             BrowserAccessibilityAndroid* node,
                             int32_t* start_index,
                             int32_t* end_index);

  // Set accessibility focus. This sends a message to the renderer to
  // asynchronously load inline text boxes for this node only, enabling more
  // accurate movement by granularities on this node.
  void SetAccessibilityFocus(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj,
                             jint id);

  // Returns true if the object is a slider.
  bool IsSlider(JNIEnv* env,
                const base::android::JavaParamRef<jobject>& obj,
                jint id);

  // Scrolls any scrollable container by about 80% of one page in the
  // given direction.
  bool Scroll(JNIEnv* env,
              const base::android::JavaParamRef<jobject>& obj,
              jint id,
              int direction);

 protected:
  // AXTreeDelegate overrides.
  void OnAtomicUpdateFinished(
      ui::AXTree* tree,
      bool root_changed,
      const std::vector<ui::AXTreeDelegate::Change>& changes) override;

  bool UseRootScrollOffsetsWhenComputingBounds() override;

 private:
  // This gives BrowserAccessibilityManager::Create access to the class
  // constructor.
  friend class BrowserAccessibilityManager;

  // A weak reference to the Java BrowserAccessibilityManager object.
  // This avoids adding another reference to BrowserAccessibilityManager and
  // preventing garbage collection.
  // Premature garbage collection is prevented by the long-lived reference in
  // ContentViewCore.
  JavaObjectWeakGlobalRef java_ref_;

  // Handle a hover event from the renderer process.
  void HandleHoverEvent(BrowserAccessibility* node);

  // See docs for set_prune_tree_for_screen_reader, above.
  bool prune_tree_for_screen_reader_;

  DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerAndroid);
};

bool RegisterBrowserAccessibilityManager(JNIEnv* env);

}

#endif  // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_ANDROID_H_
