File: view_tree_validator.mm

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (101 lines) | stat: -rw-r--r-- 3,253 bytes parent folder | download | duplicates (5)
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
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/test/view_tree_validator.h"

#include <Cocoa/Cocoa.h>

#include "base/mac/mac_util.h"
#include "base/strings/sys_string_conversions.h"

namespace {

NSArray* CollectSubviews(NSView* root) {
  NSMutableArray* subviews = [NSMutableArray arrayWithObject:root];
  for (NSView* child in root.subviews) {
    [subviews addObjectsFromArray:CollectSubviews(child)];
  }
  return subviews;
}

bool ViewsOverlap(NSView* a, NSView* b) {
  NSRect a_frame = [a convertRect:a.bounds toView:nil];
  NSRect b_frame = [b convertRect:b.bounds toView:nil];
  return NSIntersectsRect(a_frame, b_frame);
}

bool IsLocalizable(NSView* view) {
  return [view isKindOfClass:[NSControl class]] ||
         [view isKindOfClass:[NSText class]];
}

// Returns whether to expect children of |view| to perhaps not fit within its
// bounds.
bool IgnoreChildBoundsChecks(NSView* view) {
  // On macOS 10.14+, NSButton has a subview of a private helper class whose
  // bounds extend a bit outside the NSButton itself. We don't care about this
  // helper class's bounds being outside the button.
  return [view isKindOfClass:[NSButton class]];
}

}  // namespace

namespace ui {

std::optional<ViewTreeProblemDetails> ValidateViewTree(NSView* root) {
  NSArray* allViews = CollectSubviews(root);

  for (NSView* view in allViews) {
    // 1: Check that every subview's frame lies entirely inside this view's
    // bounds.
    for (NSView* child in view.subviews) {
      if (!NSContainsRect(view.bounds, child.frame) &&
          !IgnoreChildBoundsChecks(view)) {
        return std::optional<ViewTreeProblemDetails>(
            {ViewTreeProblemDetails::ProblemType::kViewOutsideParent, child,
             view});
      }
    }

    // If |view| isn't localizable, skip the rest of the checks.
    if (!IsLocalizable(view))
      continue;

    // 2: Check that every other subview either:
    //   a: doesn't overlap this view
    //   b: is a descendant of this view
    //   c: has this view as a descendant
    // note that a view is its own descendant.
    for (NSView* other in allViews) {
      if (!ViewsOverlap(view, other))
        continue;
      if ([view isDescendantOf:other] || [other isDescendantOf:view])
        continue;
      return std::optional<ViewTreeProblemDetails>(
          {ViewTreeProblemDetails::ProblemType::kViewsOverlap, view, other});
    }
  }

  return std::nullopt;
}

std::string ViewTreeProblemDetails::ToString() {
  NSString* s;
  switch (type) {
    case ProblemType::kViewOutsideParent:
      s = [NSString stringWithFormat:@"View %@ [%@] outside parent %@ [%@]",
                                     view_a, NSStringFromRect(view_a.frame),
                                     view_b, NSStringFromRect(view_b.frame)];
      break;
    case ProblemType::kViewsOverlap:
      s = [NSString stringWithFormat:@"Views %@ [%@] and %@ [%@] overlap",
                                     view_a, NSStringFromRect(view_a.frame),
                                     view_b, NSStringFromRect(view_b.frame)];
      break;
  }

  return base::SysNSStringToUTF8(s);
}

}  // namespace ui