This repository has been archived by the owner on Feb 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ability to disable the browser's context menu on web (#118194)
Enables custom context menus on web
- Loading branch information
Showing
9 changed files
with
272 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
packages/flutter/lib/src/services/browser_context_menu.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'package:flutter/foundation.dart'; | ||
|
||
import 'system_channels.dart'; | ||
|
||
/// Controls the browser's context menu on the web platform. | ||
/// | ||
/// The context menu is the menu that appears on right clicking or selecting | ||
/// text in the browser, for example. | ||
/// | ||
/// On web, by default, the browser's context menu is enabled and Flutter's | ||
/// context menus are hidden. | ||
/// | ||
/// On all non-web platforms, this does nothing. | ||
class BrowserContextMenu { | ||
BrowserContextMenu._(); | ||
|
||
static final BrowserContextMenu _instance = BrowserContextMenu._(); | ||
|
||
/// Whether showing the browser's context menu is enabled. | ||
/// | ||
/// When true, any event that the browser typically uses to trigger its | ||
/// context menu (e.g. right click) will do so. When false, the browser's | ||
/// context menu will not show. | ||
/// | ||
/// It's possible for this to be true but for the browser's context menu to | ||
/// not show due to direct manipulation of the DOM. For example, handlers for | ||
/// the browser's `contextmenu` event could be added/removed in the browser's | ||
/// JavaScript console, and this boolean wouldn't know about it. This boolean | ||
/// only indicates the results of calling [disableContextMenu] and | ||
/// [enableContextMenu] here. | ||
/// | ||
/// Defaults to true. | ||
static bool get enabled => _instance._enabled; | ||
|
||
bool _enabled = true; | ||
|
||
final MethodChannel _channel = SystemChannels.contextMenu; | ||
|
||
/// Disable the browser's context menu. | ||
/// | ||
/// By default, when the app starts, the browser's context menu is already | ||
/// enabled. | ||
/// | ||
/// This is an asynchronous action. The context menu can be considered to be | ||
/// disabled at the time that the Future resolves. [enabled] won't reflect the | ||
/// change until that time. | ||
/// | ||
/// See also: | ||
/// * [enableContextMenu], which performs the opposite operation. | ||
static Future<void> disableContextMenu() { | ||
assert(kIsWeb, 'This has no effect on platforms other than web.'); | ||
return _instance._channel.invokeMethod<void>( | ||
'disableContextMenu', | ||
).then((_) { | ||
_instance._enabled = false; | ||
}); | ||
} | ||
|
||
/// Enable the browser's context menu. | ||
/// | ||
/// By default, when the app starts, the browser's context menu is already | ||
/// enabled. Typically this method would be called after first calling | ||
/// [disableContextMenu]. | ||
/// | ||
/// This is an asynchronous action. The context menu can be considered to be | ||
/// enabled at the time that the Future resolves. [enabled] won't reflect the | ||
/// change until that time. | ||
/// | ||
/// See also: | ||
/// * [disableContextMenu], which performs the opposite operation. | ||
static Future<void> enableContextMenu() { | ||
assert(kIsWeb, 'This has no effect on platforms other than web.'); | ||
return _instance._channel.invokeMethod<void>( | ||
'enableContextMenu', | ||
).then((_) { | ||
_instance._enabled = true; | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
packages/flutter/test/services/browser_context_menu_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'package:flutter/foundation.dart'; | ||
import 'package:flutter/services.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
void main() { | ||
TestWidgetsFlutterBinding.ensureInitialized(); | ||
|
||
final List<MethodCall> log = <MethodCall>[]; | ||
|
||
Future<void> verify(AsyncCallback test, List<Object> expectations) async { | ||
log.clear(); | ||
await test(); | ||
expect(log, expectations); | ||
} | ||
|
||
group('not on web', () { | ||
test('disableContextMenu asserts', () async { | ||
try { | ||
BrowserContextMenu.disableContextMenu(); | ||
} catch (error) { | ||
expect(error, isAssertionError); | ||
} | ||
}); | ||
|
||
test('enableContextMenu asserts', () async { | ||
try { | ||
BrowserContextMenu.enableContextMenu(); | ||
} catch (error) { | ||
expect(error, isAssertionError); | ||
} | ||
}); | ||
}, | ||
skip: kIsWeb, // [intended] | ||
); | ||
|
||
group('on web', () { | ||
group('disableContextMenu', () { | ||
// Make sure the context menu is enabled (default) after the test. | ||
tearDown(() async { | ||
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, (MethodCall methodCall) { | ||
return null; | ||
}); | ||
await BrowserContextMenu.enableContextMenu(); | ||
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, null); | ||
}); | ||
|
||
test('disableContextMenu calls its platform channel method', () async { | ||
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, (MethodCall methodCall) async { | ||
log.add(methodCall); | ||
return null; | ||
}); | ||
|
||
await verify(BrowserContextMenu.disableContextMenu, <Object>[ | ||
isMethodCall('disableContextMenu', arguments: null), | ||
]); | ||
|
||
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, null); | ||
}); | ||
}); | ||
|
||
group('enableContextMenu', () { | ||
test('enableContextMenu calls its platform channel method', () async { | ||
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, (MethodCall methodCall) async { | ||
log.add(methodCall); | ||
return null; | ||
}); | ||
|
||
await verify(BrowserContextMenu.enableContextMenu, <Object>[ | ||
isMethodCall('enableContextMenu', arguments: null), | ||
]); | ||
|
||
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, null); | ||
}); | ||
}); | ||
}, | ||
skip: !kIsWeb, // [intended] | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.