File: web_menu_runner_ios.mm

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; 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,811; 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 (171 lines) | stat: -rw-r--r-- 5,951 bytes parent folder | download | duplicates (6)
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
163
164
165
166
167
168
169
170
171
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "content/browser/renderer_host/web_menu_runner_ios.h"

#include "base/strings/sys_string_conversions.h"

@interface UIContextMenuInteraction ()
- (void)_presentMenuAtLocation:(CGPoint)location;
@end

@interface WebMenuRunner () <UIContextMenuInteractionDelegate>
@end

@implementation WebMenuRunner {
  // The UIView in which the popup menu will be displayed.
  UIView* __weak _view;

  // The bounds of the select element from which the menu was triggered.
  CGRect _elementBounds;

  // The index of the selected menu item.
  size_t _selectedIndex;

  // A flag set to YES if a menu item was chosen, or NO if the menu was
  // dismissed without selecting an item.
  BOOL _menuItemWasChosen;

  // The native UIMenu object.
  UIMenu* __strong _menu;

  // Interaction for displaying a popup menu.
  UIContextMenuInteraction* __strong _selectContextMenuInteraction;

  // Delegate to handle menu select/cancel events.
  base::WeakPtr<content::MenuInteractionDelegate> _delegate;
}

- (id)initWithDelegate:(base::WeakPtr<content::MenuInteractionDelegate>)delegate
                 items:(const std::vector<blink::mojom::MenuItemPtr>&)items
          initialIndex:(int)index
              fontSize:(CGFloat)fontSize
          rightAligned:(BOOL)rightAligned {
  if ((self = [super init])) {
    _delegate = delegate;

    DCHECK_GE(index, 0);
    _selectedIndex = static_cast<size_t>(index);

    [self createMenu:items];
  }
  return self;
}

- (void)showMenuInView:(UIView*)view withBounds:(CGRect)bounds {
  _view = view;
  _elementBounds = bounds;

  _selectContextMenuInteraction =
      [[UIContextMenuInteraction alloc] initWithDelegate:self];
  [_view addInteraction:_selectContextMenuInteraction];

  // TODO(crbug.com/40274444): _presentMenuAtLocation is a private API
  // which triggers the ContextMenu immediately at a specified location. By
  // default, the ContextMenu is only triggered on long press or 3D touch. This
  // private API is needed to use because we expect the popup menu to appear
  // immediately when the user touches the <select> element area.
  [_selectContextMenuInteraction _presentMenuAtLocation:_elementBounds.origin];
}

- (void)dealloc {
  [_view removeInteraction:_selectContextMenuInteraction];
}

#pragma mark - UIContextMenuInteractionDelegate

// TODO(crbug.com/40266320): This menu is being shown with unwanted effects.
// Need to find a way to show just the menu without using private API.
- (UIContextMenuConfiguration*)contextMenuInteraction:
                                   (UIContextMenuInteraction*)interaction
                       configurationForMenuAtLocation:(CGPoint)location {
  return [UIContextMenuConfiguration
      configurationWithIdentifier:nil
                  previewProvider:nil
                   actionProvider:^UIMenu* _Nullable(
                       NSArray<UIMenuElement*>* _Nonnull suggestedActions) {
                     return self->_menu;
                   }];
}

- (UITargetedPreview*)contextMenuInteraction:
                          (UIContextMenuInteraction*)interaction
                               configuration:
                                   (UIContextMenuConfiguration*)configuration
       highlightPreviewForItemWithIdentifier:(id<NSCopying>)identifier {
  UIView* snapshotView = [_view resizableSnapshotViewFromRect:_elementBounds
                                           afterScreenUpdates:NO
                                                withCapInsets:UIEdgeInsetsZero];

  UIPreviewTarget* previewTarget = [[UIPreviewTarget alloc]
      initWithContainer:_view
                 center:CGPointMake(CGRectGetMidX(_elementBounds),
                                    CGRectGetMidY(_elementBounds))];

  return
      [[UITargetedPreview alloc] initWithView:snapshotView
                                   parameters:[[UIPreviewParameters alloc] init]
                                       target:previewTarget];
}

- (void)contextMenuInteraction:(UIContextMenuInteraction*)interaction
       willEndForConfiguration:(UIContextMenuConfiguration*)configuration
                      animator:(id<UIContextMenuInteractionAnimating>)animator {
  _menu = nil;
  if (!_delegate) {
    return;
  }

  if (_menuItemWasChosen) {
    _delegate->OnMenuItemSelected(_selectedIndex);
  } else {
    _delegate->OnMenuCanceled();
  }
}

#pragma mark - Private

// Creates the native UIMenu object using the provided list of menu items.
- (void)createMenu:(const std::vector<blink::mojom::MenuItemPtr>&)items {
  NSMutableArray* actions = [NSMutableArray array];

  for (size_t i = 0; i < items.size(); ++i) {
    UIAction* action = [self addItem:items[i] itemIndex:i];
    if (i == _selectedIndex) {
      action.state = UIMenuElementStateOn;
    }
    [actions addObject:action];
  }

  _menu = [UIMenu menuWithTitle:@""
                          image:nil
                     identifier:nil
                        options:UIMenuOptionsDisplayInline
                       children:actions];
}

// Worker function used during initialization.
- (UIAction*)addItem:(const blink::mojom::MenuItemPtr&)item
           itemIndex:(size_t)index {
  NSString* title = base::SysUTF8ToNSString(item->label.value_or(""));
  UIAction* itemAction =
      [UIAction actionWithTitle:title
                          image:nil
                     identifier:nil
                        handler:^(__kindof UIAction* action) {
                          [self menuItemSelected:index];
                        }];

  return itemAction;
}

// A callback for the menu controller object to call when an item is selected
// from the menu. This is not called if the menu is dismissed without a
// selection.
- (void)menuItemSelected:(size_t)index {
  _menuItemWasChosen = YES;
  _selectedIndex = index;
}

@end  // WebMenuRunner