DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (81f420f057e4)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#import <Cocoa/Cocoa.h>

#include "Platform.h"
#include "ProxyAccessible.h"
#include "DocAccessibleParent.h"
#include "mozTableAccessible.h"

#include "nsAppShell.h"

namespace mozilla {
namespace a11y {

// Mac a11y whitelisting
static bool sA11yShouldBeEnabled = false;

bool ShouldA11yBeEnabled() {
  EPlatformDisabledState disabledState = PlatformDisabledState();
  return (disabledState == ePlatformIsForceEnabled) ||
         ((disabledState == ePlatformIsEnabled) && sA11yShouldBeEnabled);
}

void PlatformInit() {}

void PlatformShutdown() {}

void ProxyCreated(ProxyAccessible* aProxy, uint32_t) {
  // Pass in dummy state for now as retrieving proxy state requires IPC.
  // Note that we can use ProxyAccessible::IsTable* functions here because they
  // do not use IPC calls but that might change after bug 1210477.
  Class type;
  if (aProxy->IsTable())
    type = [mozTableAccessible class];
  else if (aProxy->IsTableRow())
    type = [mozTableRowAccessible class];
  else if (aProxy->IsTableCell())
    type = [mozTableCellAccessible class];
  else
    type = GetTypeFromRole(aProxy->Role());

  uintptr_t accWrap = reinterpret_cast<uintptr_t>(aProxy) | IS_PROXY;
  mozAccessible* mozWrapper = [[type alloc] initWithAccessible:accWrap];
  aProxy->SetWrapper(reinterpret_cast<uintptr_t>(mozWrapper));

  mozAccessible* nativeParent = nullptr;
  if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
    // If proxy is top level, the parent we need to invalidate the children of
    // will be a non-remote accessible.
    Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
    if (outerDoc) {
      nativeParent = GetNativeFromGeckoAccessible(outerDoc);
    }
  } else {
    // Non-top level proxies need proxy parents' children invalidated.
    ProxyAccessible* parent = aProxy->Parent();
    MOZ_ASSERT(parent ||
                   // It's expected that an OOP iframe might not have a parent yet.
                   (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevelInContentProcess()),
               "a non-top-level proxy is missing a parent?");
    nativeParent = parent ? GetNativeFromProxy(parent) : nullptr;
  }

  if (nativeParent) {
    [nativeParent invalidateChildren];
  }
}

void ProxyDestroyed(ProxyAccessible* aProxy) {
  mozAccessible* nativeParent = nil;
  if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
    // Invalidate native parent in parent process's children on proxy destruction
    Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
    if (outerDoc) {
      nativeParent = GetNativeFromGeckoAccessible(outerDoc);
    }
  } else {
    if (!aProxy->Document()->IsShutdown()) {
      // Only do if the document has not been shut down, else parent will return
      // garbage since we don't shut down children from top down.
      ProxyAccessible* parent = aProxy->Parent();
      // Invalidate proxy parent's children.
      if (parent) {
        nativeParent = GetNativeFromProxy(parent);
      }
    }
  }

  mozAccessible* wrapper = GetNativeFromProxy(aProxy);
  [wrapper expire];
  [wrapper release];
  aProxy->SetWrapper(0);

  if (nativeParent) {
    [nativeParent invalidateChildren];
  }
}

void ProxyEvent(ProxyAccessible* aProxy, uint32_t aEventType) {
  if (aEventType == nsIAccessibleEvent::EVENT_REORDER && aProxy->ChildrenCount() == 1 &&
      aProxy->ChildAt(0)->IsDoc()) {
    // This is a remote OuterDocAccessible. The reorder event indicates that Its
    // embedded document has been added or changed. If the document itself is
    // an existing Accessible, ProxyCreated won't have been called, so we won't
    // have invalidated native children. This can happen for in-process iframes
    // if the OuterDocAccessible is re-created (e.g. due to layout reflow).
    // It always happens for out-of-process iframes, as we must always call
    // ProxyCreated before DocAccessibleParent::AddChildDoc for those.
    mozAccessible* wrapper = GetNativeFromProxy(aProxy);
    if (wrapper) {
      [wrapper invalidateChildren];
    }
  }

  // ignore everything but focus-changed, value-changed, caret and selection
  // events for now.
  if (aEventType != nsIAccessibleEvent::EVENT_FOCUS &&
      aEventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
      aEventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE &&
      aEventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
      aEventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED)
    return;

  mozAccessible* wrapper = GetNativeFromProxy(aProxy);
  if (wrapper) FireNativeEvent(wrapper, aEventType);
}

void ProxyStateChangeEvent(ProxyAccessible* aProxy, uint64_t, bool) {
  // mac doesn't care about state change events
}

void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset) {
  mozAccessible* wrapper = GetNativeFromProxy(aTarget);
  if (wrapper) [wrapper selectedTextDidChange];
}

void ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t, bool, bool) {}

void ProxyShowHideEvent(ProxyAccessible*, ProxyAccessible*, bool, bool) {}

void ProxySelectionEvent(ProxyAccessible*, ProxyAccessible*, uint32_t) {}
}  // namespace a11y
}  // namespace mozilla

@interface GeckoNSApplication (a11y)
- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
@end

@implementation GeckoNSApplication (a11y)

- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
  if ([attribute isEqualToString:@"AXEnhancedUserInterface"])
    mozilla::a11y::sA11yShouldBeEnabled = ([value intValue] == 1);

  return [super accessibilitySetValue:value forAttribute:attribute];
}

@end