From 195a31324587fffa002caaffa123c2cd5816381c Mon Sep 17 00:00:00 2001 From: Bruno Leroux Date: Tue, 19 Sep 2023 11:40:18 +0200 Subject: [PATCH] =?UTF-8?q?[macOS,iOS]=20Expose=20channel=20buffers=20'res?= =?UTF-8?q?ize'=20and=20'overflow'=20control=20co=E2=80=A6=20(flutter/engi?= =?UTF-8?q?ne#44848)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR update the helper function that invokes the control channel 'resize' command (previous implementation relied on a deprecated format). It also adds a similar helper function for the 'overflow' commands exposed by the control channel. See: https://github.com/flutter/engine/blob/c00c02203662671676c5678ced4cc93f9d170df6/lib/ui/channel_buffers.dart#L302-L309 ## Related Issue iOS and macOS implementation for https://github.com/flutter/flutter/issues/132386 Similar implementations: - Android: https://github.com/flutter/engine/pull/44434 - Linux: https://github.com/flutter/engine/pull/44636 ## Tests Adds two tests. --- .../framework/Headers/FlutterChannels.h | 37 ++++++++++++++ .../framework/Source/FlutterChannels.mm | 49 ++++++++++++++++++- .../framework/Source/FlutterChannelsTest.m | 38 ++++++++++++-- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h b/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h index 9b84cd64c71..cdbf140091e 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h @@ -143,9 +143,46 @@ FLUTTER_DARWIN_EXPORT * Adjusts the number of messages that will get buffered when sending messages to * channels that aren't fully set up yet. For example, the engine isn't running * yet or the channel's message handler isn't set up on the Dart side yet. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param newSize The number of messages that will get buffered. + */ ++ (void)resizeChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger + size:(NSInteger)newSize; + +/** + * Adjusts the number of messages that will get buffered when sending messages to + * channels that aren't fully set up yet. For example, the engine isn't running + * yet or the channel's message handler isn't set up on the Dart side yet. + * + * @param newSize The number of messages that will get buffered. */ - (void)resizeChannelBuffer:(NSInteger)newSize; +/** + * Defines whether the channel should show warning messages when discarding messages + * due to overflow. + * + * @param warns When false, the channel is expected to overflow and warning messages + * will not be shown. + * @param name The channel name. + * @param messenger The binary messenger. + */ ++ (void)setWarnsOnOverflow:(BOOL)warns + forChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger; + +/** + * Defines whether the channel should show warning messages when discarding messages + * due to overflow. + * + * @param warns When false, the channel is expected to overflow and warning messages + * will not be shown. + */ +- (void)setWarnsOnOverflow:(BOOL)warns; + @end /** diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm index f360144d374..78d5c084042 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm @@ -9,12 +9,41 @@ FLUTTER_ASSERT_ARC #pragma mark - Basic message channel static NSString* const kFlutterChannelBuffersChannel = @"dev.flutter/channel-buffers"; +static NSString* const kResizeMethod = @"resize"; +static NSString* const kOverflowMethod = @"overflow"; static void ResizeChannelBuffer(NSObject* binaryMessenger, NSString* channel, NSInteger newSize) { - NSString* messageString = [NSString stringWithFormat:@"resize\r%@\r%@", channel, @(newSize)]; - NSData* message = [messageString dataUsingEncoding:NSUTF8StringEncoding]; + NSCAssert(newSize >= 0, @"Channel buffer size must be non-negative"); + // Cast newSize to int because the deserialization logic handles only 32 bits values, + // see + // https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L495. + NSArray* args = @[ channel, @(static_cast(newSize)) ]; + FlutterMethodCall* resizeMethodCall = [FlutterMethodCall methodCallWithMethodName:kResizeMethod + arguments:args]; + NSObject* codec = [FlutterStandardMethodCodec sharedInstance]; + NSData* message = [codec encodeMethodCall:resizeMethodCall]; + [binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message]; +} + +/** + * Defines whether a channel should show warning messages when discarding messages + * due to overflow. + * + * @param binaryMessenger The binary messenger. + * @param channel The channel name. + * @param warns When false, the channel is expected to overflow and warning messages + * will not be shown. + */ +static void SetWarnsOnOverflow(NSObject* binaryMessenger, + NSString* channel, + BOOL warns) { + FlutterMethodCall* overflowMethodCall = + [FlutterMethodCall methodCallWithMethodName:kOverflowMethod + arguments:@[ channel, @(!warns) ]]; + NSObject* codec = [FlutterStandardMethodCodec sharedInstance]; + NSData* message = [codec encodeMethodCall:overflowMethodCall]; [binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message]; } @@ -114,10 +143,26 @@ static FlutterBinaryMessengerConnection SetMessageHandler( _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue); } ++ (void)resizeChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger + size:(NSInteger)newSize { + ResizeChannelBuffer(messenger, name, newSize); +} + - (void)resizeChannelBuffer:(NSInteger)newSize { ResizeChannelBuffer(_messenger, _name, newSize); } ++ (void)setWarnsOnOverflow:(BOOL)warns + forChannelWithName:(NSString*)name + binaryMessenger:(NSObject*)messenger { + SetWarnsOnOverflow(messenger, name, warns); +} + +- (void)setWarnsOnOverflow:(BOOL)warns { + SetWarnsOnOverflow(_messenger, _name, warns); +} + @end #pragma mark - Method channel diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m index 767c762d864..fef4a1075cc 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m @@ -147,7 +147,7 @@ FLUTTER_ASSERT_ARC } - (void)testResize { - NSString* channelName = @"foo"; + NSString* channelName = @"flutter/test"; id binaryMessenger = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger)); id codec = OCMProtocolMock(@protocol(FlutterMethodCodec)); FlutterBasicMessageChannel* channel = @@ -156,11 +156,39 @@ FLUTTER_ASSERT_ARC codec:codec]; XCTAssertNotNil(channel); - NSString* expectedMessageString = - [NSString stringWithFormat:@"resize\r%@\r%@", channelName, @100]; - NSData* expectedMessage = [expectedMessageString dataUsingEncoding:NSUTF8StringEncoding]; + // The expected content was created from the following Dart code: + // MethodCall call = MethodCall('resize', ['flutter/test',3]); + // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List(); + const unsigned char bytes[] = {7, 6, 114, 101, 115, 105, 122, 101, 12, 2, + 7, 12, 102, 108, 117, 116, 116, 101, 114, 47, + 116, 101, 115, 116, 3, 3, 0, 0, 0}; + NSData* expectedMessage = [NSData dataWithBytes:bytes length:sizeof(bytes)]; + OCMExpect([binaryMessenger sendOnChannel:@"dev.flutter/channel-buffers" message:expectedMessage]); - [channel resizeChannelBuffer:100]; + [channel resizeChannelBuffer:3]; + OCMVerifyAll(binaryMessenger); + [binaryMessenger stopMocking]; +} + +- (bool)testSetWarnsOnOverflow { + NSString* channelName = @"flutter/test"; + id binaryMessenger = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger)); + id codec = OCMProtocolMock(@protocol(FlutterMethodCodec)); + FlutterBasicMessageChannel* channel = + [[FlutterBasicMessageChannel alloc] initWithName:channelName + binaryMessenger:binaryMessenger + codec:codec]; + XCTAssertNotNil(channel); + + // The expected content was created from the following Dart code: + // MethodCall call = MethodCall('overflow',['flutter/test', true]); + // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List(); + const unsigned char bytes[] = {7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 12, + 102, 108, 117, 116, 116, 101, 114, 47, 116, 101, 115, 116, 1}; + NSData* expectedMessage = [NSData dataWithBytes:bytes length:sizeof(bytes)]; + + OCMExpect([binaryMessenger sendOnChannel:@"dev.flutter/channel-buffers" message:expectedMessage]); + [channel setWarnsOnOverflow:NO]; OCMVerifyAll(binaryMessenger); [binaryMessenger stopMocking]; }