From 3ce42a2a4e4ee5c23ff044b8804596c0f5654c00 Mon Sep 17 00:00:00 2001 From: zealotchen Date: Mon, 13 May 2024 18:38:39 +0800 Subject: [PATCH 01/28] feat(action): fix v8 build error --- .github/workflows/3rd_prebuilt_v8.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/3rd_prebuilt_v8.yml b/.github/workflows/3rd_prebuilt_v8.yml index 6b1f2dc424d..b7774e0461a 100644 --- a/.github/workflows/3rd_prebuilt_v8.yml +++ b/.github/workflows/3rd_prebuilt_v8.yml @@ -192,6 +192,7 @@ jobs: - name: Sync third_party working-directory: ./v8 run: | + export PATH=/usr/local/opt/depot_tools:$PATH echo "target_os = ['android']" >> ../.gclient gclient sync -D - name: Prepare android_ndk @@ -218,10 +219,12 @@ jobs: - name: Generate ${{ matrix.arch }} working-directory: ./v8 run: | + export PATH=/usr/local/opt/depot_tools:$PATH gn gen out --args="target_os=\"android\" target_cpu=\"${{ matrix.cpu }}\" v8_target_cpu=\"${{ matrix.cpu }}\" android_ndk_root=\"${ANDROID_NDK_HOME}\" is_component_build=false v8_monolithic=true android32_ndk_api_level=21 android64_ndk_api_level=21 clang_use_chrome_plugins=false use_thin_lto=false use_custom_libcxx=false ${{ github.event.inputs.build_type == 'release' && 'is_debug=false is_official_build=true' || 'is_debug=true' }} ${{ github.event.inputs.build_args }}" - name: Compile ${{ matrix.arch }} working-directory: ./v8 run: | + export PATH=/usr/local/opt/depot_tools:$PATH ninja -C out v8_monolith - name: Prepare package working-directory: ./v8/out From e501e9334e5cecacd289ff0ab068c51887c24a07 Mon Sep 17 00:00:00 2001 From: siguangli Date: Mon, 13 May 2024 20:30:53 +0800 Subject: [PATCH 02/28] fix(android): correct move node impl with different pid --- .../cpp/src/renderer/native_render_manager.cc | 6 +-- .../renderer/NativeRenderDelegate.java | 2 +- .../renderer/NativeRenderProvider.java | 5 +-- .../com/tencent/renderer/NativeRenderer.java | 43 ++++++++++++++----- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc b/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc index 543d6dd08bf..18457c3fb42 100644 --- a/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc +++ b/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc @@ -259,7 +259,6 @@ void NativeRenderManager::MoveRenderNode(std::weak_ptr root_node, auto len = nodes.size(); footstone::value::HippyValue::HippyValueArrayType dom_node_array; dom_node_array.resize(len); - uint32_t pid; for (uint32_t i = 0; i < len; i++) { const auto& render_info = nodes[i]->GetRenderInfo(); footstone::value::HippyValue::HippyValueObjectType dom_node; @@ -267,7 +266,6 @@ void NativeRenderManager::MoveRenderNode(std::weak_ptr root_node, dom_node[kPid] = footstone::value::HippyValue(render_info.pid); dom_node[kIndex] = footstone::value::HippyValue(render_info.index); dom_node_array[i] = dom_node; - pid = render_info.pid; } serializer_->WriteValue(HippyValue(dom_node_array)); std::pair buffer_pair = serializer_->Release(); @@ -286,12 +284,12 @@ void NativeRenderManager::MoveRenderNode(std::weak_ptr root_node, FOOTSTONE_LOG(ERROR) << "CallNativeMethod j_class error"; return; } - jmethodID j_method_id = j_env->GetMethodID(j_class, "moveNode", "(II[B)V"); + jmethodID j_method_id = j_env->GetMethodID(j_class, "moveNode", "(I[B)V"); if (!j_method_id) { FOOTSTONE_LOG(ERROR) << "moveNode" << " j_method_id error"; return; } - j_env->CallVoidMethod(j_object, j_method_id, root->GetId(), pid, j_buffer); + j_env->CallVoidMethod(j_object, j_method_id, root->GetId(), j_buffer); JNIEnvironment::ClearJEnvException(j_env); j_env->DeleteLocalRef(j_buffer); j_env->DeleteLocalRef(j_class); diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java index 40009a013aa..372fbb2bcaf 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java @@ -30,7 +30,7 @@ public interface NativeRenderDelegate extends RenderExceptionHandler, RenderLogH void moveNode(int rootId, int[] ids, int newPid, int oldPid, int insertIndex) throws NativeRenderException; - void moveNode(int rootId, int pid, @NonNull List list) throws NativeRenderException; + void moveNode(int rootId, @NonNull List list) throws NativeRenderException; void updateLayout(int rootId, @NonNull List list) throws NativeRenderException; diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java index 4a38920618a..e2a6a55e64e 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java @@ -213,17 +213,16 @@ public void moveNode(int rootId, int[] ids, int newPid, int oldPid, int insertIn * Adjust the order of child nodes under the same parent node * * @param rootId the root node id - * @param pid the parent node id * @param buffer the byte array serialize by native (C++) */ @CalledByNative @SuppressWarnings("unused") - public void moveNode(int rootId, int pid, byte[] buffer) { + public void moveNode(int rootId, byte[] buffer) { NativeRenderDelegate renderDelegate = mRenderDelegateRef.get(); if (renderDelegate != null) { try { final List list = bytesToArgument(ByteBuffer.wrap(buffer)); - renderDelegate.moveNode(rootId, pid, list); + renderDelegate.moveNode(rootId, list); } catch (NativeRenderException e) { renderDelegate.handleRenderException(e); } diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java index f9ad86af0e7..bf2e3586877 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java @@ -656,16 +656,39 @@ public void moveNode(final int rootId, final int[] ids, final int newPid, final } @Override - public void moveNode(final int rootId, final int pid, @NonNull final List list) { + public void moveNode(final int rootId, @NonNull final List list) { if (LogUtils.isDebugMode()) { - LogUtils.d(TAG, "moveNode: pid " + pid + ", node list " + list + "\n "); + LogUtils.d(TAG, "moveNode: node list " + list + "\n "); } - VirtualNode parent = mVirtualNodeManager.getVirtualNode(rootId, pid); - if (parent == null) { - addUITask(() -> mRenderManager.moveNode(rootId, pid, list)); - } else { - mVirtualNodeManager.moveNode(rootId, parent, list); - addUITask(() -> mRenderManager.onMoveVirtualNode(rootId, pid, list)); + final Map> nodeMap = new HashMap<>(); + for (int i = 0; i < list.size(); i++) { + final Map moveNodeInfo = ArrayUtils.getMapValue(list, i); + if (moveNodeInfo == null) { + continue; + } + final Integer pid = MapUtils.getIntValue(moveNodeInfo, NODE_PID, INVALID_NODE_ID); + if (pid == INVALID_NODE_ID) { + continue; + } + List nodeList = nodeMap.get(pid); + if (nodeList == null) { + nodeList = new ArrayList<>(); + nodeList.add(moveNodeInfo); + nodeMap.put(pid, nodeList); + } else { + nodeList.add(moveNodeInfo); + } + } + for (Entry> entry : nodeMap.entrySet()) { + final Integer pid = entry.getKey(); + final List value = entry.getValue(); + VirtualNode parent = mVirtualNodeManager.getVirtualNode(rootId, pid); + if (parent == null) { + addUITask(() -> mRenderManager.moveNode(rootId, pid, value)); + } else { + mVirtualNodeManager.moveNode(rootId, parent, value); + addUITask(() -> mRenderManager.onMoveVirtualNode(rootId, pid, value)); + } } } @@ -738,8 +761,8 @@ public void updateEventListener(final int rootId, @NonNull List eventLis TAG + ": updateEventListener: invalid negative id=" + nodeId); } if (LogUtils.isDebugMode()) { - LogUtils.d(TAG, - "updateEventListener: id " + nodeId + ", eventProps " + eventProps + "\n "); +// LogUtils.d(TAG, +// "updateEventListener: id " + nodeId + ", eventProps " + eventProps + "\n "); } mVirtualNodeManager.updateEventListener(rootId, nodeId, eventProps); taskList.add(() -> mRenderManager.updateEventListener(rootId, nodeId, eventProps)); From 2805d03737bd3c954147f77a32f24eedb919e55b Mon Sep 17 00:00:00 2001 From: siguangli Date: Mon, 13 May 2024 20:34:53 +0800 Subject: [PATCH 03/28] fix(android): image span draw transparent color when no src set --- .../main/java/com/tencent/renderer/node/ImageVirtualNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java index 4955f8773b3..12f5d3335ff 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java @@ -144,7 +144,7 @@ protected TextImageSpan createImageSpan() { } } if (drawable == null) { - drawable = new ColorDrawable(Color.WHITE); + drawable = new ColorDrawable(Color.TRANSPARENT); } drawable.setBounds(0, 0, mWidth, mHeight); return new TextImageSpan(drawable, mUrl, this, mNativeRenderer); From 42d13c58f82e16f7c75ddcf5b941c0da806dcbc3 Mon Sep 17 00:00:00 2001 From: siguangli Date: Mon, 13 May 2024 20:38:57 +0800 Subject: [PATCH 04/28] refactor(docs): add android snapshot version description --- docs/development/android-3.0-integration-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/android-3.0-integration-guidelines.md b/docs/development/android-3.0-integration-guidelines.md index b2613b69ac1..781103152d5 100644 --- a/docs/development/android-3.0-integration-guidelines.md +++ b/docs/development/android-3.0-integration-guidelines.md @@ -22,7 +22,7 @@ 2. Maven 集成 - - 查询 [Maven Central Hippy 版本](https://search.maven.org/search?q=com.tencent.hippy),其中 `hippy-release` 为 `release` 版本(不携带 `inspector`),`hippy-debug` 为 `debug` 版本 + - 查询 [Maven Central Hippy 版本](https://search.maven.org/search?q=com.tencent.hippy),其中 `hippy-release` 为 `release` 版本,`hippy-debug` 为 `debug` 版本,`hippy-snapshot` 为 `beta` 版本 - 配置 build.gradle From 9cf8348333ef203db9bc3cd2ad2b56f920f609b9 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 17 Apr 2024 15:40:51 +0800 Subject: [PATCH 05/28] chore(ios): improve parameter mismatch tips --- .../ios/base/modules/HippyModuleMethod.mm | 33 +++++++++---------- modules/ios/base/HippyLog.mm | 2 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/framework/ios/base/modules/HippyModuleMethod.mm b/framework/ios/base/modules/HippyModuleMethod.mm index 0ac4f61637e..fd6f4edb7de 100644 --- a/framework/ios/base/modules/HippyModuleMethod.mm +++ b/framework/ios/base/modules/HippyModuleMethod.mm @@ -464,23 +464,22 @@ - (id)invokeWithBridge:(HippyBridge *)bridge module:(id)module arguments:(NSArra %@ on a module of class %@", [self methodName], [module class]); // Safety check - if (arguments.count != _argumentBlocks.count) { - NSInteger actualCount = arguments.count; - NSInteger expectedCount = _argumentBlocks.count; - - // Subtract the implicit Promise resolver and rejecter functions for implementations of async functions - if (self.functionType == HippyFunctionTypePromise) { - actualCount -= 2; - expectedCount -= 2; - } - - HippyLogError(@"%@.%@ was called with %ld arguments, but expects %ld. \ - If you haven\'t changed this method " - @"yourself, this usually means that \ - your versions of the native code and JavaScript code are out " - @"of sync. \ - Updating both should make this error go away.", - HippyBridgeModuleNameForClass(_moduleClass), self.JSMethodName, (long)actualCount, (long)expectedCount); + NSInteger actualCount = arguments.count; + NSInteger expectedCount = _argumentBlocks.count; + BOOL isArgumentsMismatch = NO; + if (actualCount > expectedCount || + (self.functionType == HippyFunctionTypePromise && actualCount < expectedCount - 2)) { + isArgumentsMismatch = YES; + } + if (isArgumentsMismatch) { + HippyLogError(@"%@.%@ was called with %lld arguments but expects %lld arguments. " + @"If you haven\'t changed this method yourself, this usually means that " + @"your versions of the native code and JavaScript code are out of sync. " + @"Updating both should make this error go away.", + HippyBridgeModuleNameForClass(_moduleClass), + self.JSMethodName, + (long long)actualCount, + (long long)expectedCount); } } diff --git a/modules/ios/base/HippyLog.mm b/modules/ios/base/HippyLog.mm index 16dbe0790f6..33d4489932d 100644 --- a/modules/ios/base/HippyLog.mm +++ b/modules/ios/base/HippyLog.mm @@ -184,7 +184,7 @@ void HippyLogNativeInternal(HippyLogLevel level, const char *fileName, int lineN // Call log function if (logFunction) { - logFunction(level, HippyLogSourceNative, fileName ? @(fileName) : nil, lineNumber > 0 ? @(lineNumber) : nil, message); + logFunction(level, HippyLogSourceNative, fileName ? @(fileName) : nil, @(lineNumber), message); } #if HIPPY_DEBUG From 1d48a6a90201aa572b5d0aaec80fa89f6736f05b Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 18 Apr 2024 16:06:44 +0800 Subject: [PATCH 06/28] refactor(ios): Improve compatibility of imageProvider --- framework/ios/base/bridge/HippyBridge.h | 10 ++--- framework/ios/base/bridge/HippyBridge.mm | 42 ++++++++++--------- .../imageloader/HippyImageLoaderModule.mm | 2 +- .../component/image/HippyImageViewManager.mm | 2 +- .../component/view/HippyViewManager.mm | 2 +- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index 9b37f2a9ffd..a19b26df1af 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -170,12 +170,12 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); /// - Parameter imageLoader: id - (void)setCustomImageLoader:(id)imageLoader; -/** - * Image provider method - * Users adds or obtains image providers in the following methods - */ +/// Get all classes that confirms to HippyImageProviderProtocol +@property (nonatomic, strong, nonnull, readonly) NSArray> *imageProviders; + +/// Add a custom ImageProvider class. +/// - Parameter cls: class confirms to HippyImageProviderProtocol - (void)addImageProviderClass:(Class)cls; -- (NSArray> *)imageProviderClasses; #pragma mark - diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index c6b6b166692..815e7208f1b 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -133,7 +133,6 @@ static inline void registerLogDelegateToHippyCore() { @interface HippyBridge() { - NSMutableArray> *_imageProviders; __weak id _methodInterceptor; HippyModulesSetup *_moduleSetup; __weak NSOperation *_lastOperation; @@ -176,6 +175,7 @@ @implementation HippyBridge @synthesize renderManager = _renderManager; @synthesize imageLoader = _imageLoader; +@synthesize imageProviders = _imageProviders; dispatch_queue_t HippyJSThread; @@ -371,25 +371,6 @@ - (HippyModuleData *)moduleDataForName:(NSString *)moduleName { return nil; } -- (void)addImageProviderClass:(Class)cls { - HippyAssertParam(cls); - @synchronized (self) { - if (!_imageProviders) { - _imageProviders = [NSMutableArray array]; - } - [_imageProviders addObject:cls]; - } -} - -- (NSArray> *)imageProviderClasses { - @synchronized (self) { - if (!_imageProviders) { - _imageProviders = [NSMutableArray array]; - } - return [_imageProviders copy]; - } -} - - (NSArray *)modulesConformingToProtocol:(Protocol *)protocol { NSMutableArray *modules = [NSMutableArray new]; for (Class moduleClass in self.moduleClasses) { @@ -432,6 +413,27 @@ - (void)setCustomImageLoader:(id)imageLoader { } } +- (NSArray> *)imageProviders { + @synchronized (self) { + if (!_imageProviders) { + NSMutableArray *moduleClasses = [NSMutableArray new]; + for (Class moduleClass in self.moduleClasses) { + if ([moduleClass conformsToProtocol:@protocol(HippyImageProviderProtocol)]) { + [moduleClasses addObject:moduleClass]; + } + } + _imageProviders = moduleClasses; + } + return [_imageProviders copy]; + } +} + +- (void)addImageProviderClass:(Class)cls { + HippyAssertParam(cls); + @synchronized (self) { + _imageProviders = [self.imageProviders arrayByAddingObject:cls]; + } +} #pragma mark - Debug Reload diff --git a/framework/ios/module/imageloader/HippyImageLoaderModule.mm b/framework/ios/module/imageloader/HippyImageLoaderModule.mm index a908df9ad1f..37926663ad8 100644 --- a/framework/ios/module/imageloader/HippyImageLoaderModule.mm +++ b/framework/ios/module/imageloader/HippyImageLoaderModule.mm @@ -47,7 +47,7 @@ @implementation HippyImageLoaderModule @synthesize bridge = _bridge; - (id)imageProviderForData:(NSData *)data { - NSArray> *providers = [self.bridge imageProviderClasses]; + NSArray> *providers = [self.bridge imageProviders]; for (Class cls in providers) { if ([cls canHandleData:data]) { id object = [[(Class)cls alloc] init]; diff --git a/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm b/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm index 9f925f814db..629be77135e 100644 --- a/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm +++ b/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm @@ -134,7 +134,7 @@ - (void)loadImageSource:(NSString *)path forView:(HippyImageView *)view { HippyBridge *bridge = strongSelf.bridge; if (bridge) { id imageProvider = nil; - for (Class cls in [bridge imageProviderClasses]) { + for (Class cls in [bridge imageProviders]) { if ([cls canHandleData:data]) { imageProvider = [[(Class)cls alloc] init]; break; diff --git a/renderer/native/ios/renderer/component/view/HippyViewManager.mm b/renderer/native/ios/renderer/component/view/HippyViewManager.mm index 3b28c925fee..9d5c38ee6bd 100644 --- a/renderer/native/ios/renderer/component/view/HippyViewManager.mm +++ b/renderer/native/ios/renderer/component/view/HippyViewManager.mm @@ -296,7 +296,7 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view { HippyBridge *bridge = strongSelf.bridge; if (bridge) { id imageProvider = nil; - for (Class cls in [bridge imageProviderClasses]) { + for (Class cls in [bridge imageProviders]) { if ([cls canHandleData:data]) { imageProvider = [[(Class)cls alloc] init]; break; From 672fb57ef61c539e7e8e9247a34d0ee3ba93cde3 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 28 Mar 2024 20:51:29 +0800 Subject: [PATCH 07/28] fix(ios): add ResponseSenderBlock type support for component parameter --- .../ios/base/modules/HippyModuleMethod.mm | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/framework/ios/base/modules/HippyModuleMethod.mm b/framework/ios/base/modules/HippyModuleMethod.mm index fd6f4edb7de..65bf27b0df2 100644 --- a/framework/ios/base/modules/HippyModuleMethod.mm +++ b/framework/ios/base/modules/HippyModuleMethod.mm @@ -293,14 +293,26 @@ - (void)processMethodSignature { } } } else if ([typeName isEqualToString:@"HippyResponseSenderBlock"]) { - HIPPY_ARG_BLOCK(if (HIPPY_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) { - HippyLogArgumentError(weakSelf, index, json, "should be a function"); - return NO; - } - __weak HippyBridge *weakBridge = bridge; - Hippy_BLOCK_ARGUMENT(^(NSArray *args) { - enqueueBlockCallback(weakBridge, weakSelf, json, args); - });) + HIPPY_ARG_BLOCK( + if (!json) { + HippyLogArgumentError(weakSelf, index, json, "should be a response sender function"); + return NO; + } + id blockArg = nil; + if (![json isKindOfClass:[NSNumber class]]) { + // In Hippy3.0, Dom Nodes call function by method name directly, + // so it is not a Number anymore. + // See NativeRenderManager::CallFunction() for more. + // TODO: add more type check for safe + blockArg = json; + } else { + __weak HippyBridge *weakBridge = bridge; + blockArg = ^(NSArray *args){ + enqueueBlockCallback(weakBridge, weakSelf, json, args); + }; + } + Hippy_BLOCK_ARGUMENT(blockArg); + ) } else if ([typeName isEqualToString:@"HippyResponseErrorBlock"]) { HIPPY_ARG_BLOCK(if (HIPPY_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) { HippyLogArgumentError(weakSelf, index, json, "should be a function"); @@ -325,6 +337,7 @@ - (void)processMethodSignature { // In Hippy3.0, Dom Nodes call function by method name directly, // so it is not a Number anymore. // See NativeRenderManager::CallFunction() for more. + // TODO: add more type check for safe blockArg = json; } else { __weak HippyBridge *weakBridge = bridge; From 4bd40e81144a5955cef31c8ef84f19e4dce1cdff Mon Sep 17 00:00:00 2001 From: wwwcg Date: Fri, 19 Apr 2024 17:10:50 +0800 Subject: [PATCH 08/28] refactor(ios): increase robustness of HippyUrl process --- driver/js/src/napi/jsc/jsc_ctx.cc | 1 + framework/ios/base/bridge/HippyBridge.h | 4 +- hippy.podspec | 1 + modules/ios/base/HippyDefines.h | 10 +++ modules/ios/base/HippyUtils.m | 10 +-- modules/vfs/ios/VFSUriLoader.mm | 7 +- tests/ios/HippyUtilsTest.m | 90 +++++++++++++++++++++++++ 7 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 tests/ios/HippyUtilsTest.m diff --git a/driver/js/src/napi/jsc/jsc_ctx.cc b/driver/js/src/napi/jsc/jsc_ctx.cc index c7d1be501ce..25f1d9f8a83 100644 --- a/driver/js/src/napi/jsc/jsc_ctx.cc +++ b/driver/js/src/napi/jsc/jsc_ctx.cc @@ -1149,6 +1149,7 @@ std::shared_ptr JSCCtx::RunScript(const string_view& data, if (exception) { SetException(std::make_shared(context_, exception)); + FOOTSTONE_LOG(ERROR) << GetExceptionMessage(exception_); return nullptr; } diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index a19b26df1af..4b5b9e18212 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -94,7 +94,7 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); /// /// Note: 多个bridge使用相同的共享engineKey时,只有全部bridge实例销毁时engine资源才将释放,因此,请注意合理使用,避免出现意外的内存泄漏。 /// 传空时默认不共享,SDK内部默认分配一随机key。 -- (instancetype)initWithDelegate:(id)delegate +- (instancetype)initWithDelegate:(nullable id)delegate moduleProvider:(nullable HippyBridgeModuleProviderBlock)block launchOptions:(nullable NSDictionary *)launchOptions executorKey:(nullable NSString *)executorKey; @@ -112,7 +112,7 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); /// /// Note: 多个bridge使用相同的共享engineKey时,只有全部bridge实例销毁时engine资源才将释放,因此,请注意合理使用,避免出现意外的内存泄漏。 /// 传空时默认不共享,SDK内部默认分配一随机key。 -- (instancetype)initWithDelegate:(id)delegate +- (instancetype)initWithDelegate:(nullable id)delegate bundleURL:(nullable NSURL *)bundleURL moduleProvider:(nullable HippyBridgeModuleProviderBlock)block launchOptions:(nullable NSDictionary *)launchOptions diff --git a/hippy.podspec b/hippy.podspec index 1d5142f4c90..3b48f1bbab1 100644 --- a/hippy.podspec +++ b/hippy.podspec @@ -167,6 +167,7 @@ Pod::Spec.new do |s| 'GCC_ENABLE_CPP_EXCEPTIONS' => false, 'GCC_ENABLE_CPP_RTTI' => false, } + iosvfs.dependency 'hippy/Base' iosvfs.dependency 'hippy/VFS' iosvfs.dependency 'hippy/Footstone' iosvfs.dependency 'hippy/FootstoneUtils' diff --git a/modules/ios/base/HippyDefines.h b/modules/ios/base/HippyDefines.h index 2b12aff1569..46b32e34a82 100644 --- a/modules/ios/base/HippyDefines.h +++ b/modules/ios/base/HippyDefines.h @@ -85,6 +85,16 @@ method NS_UNAVAILABLE { _Pragma("clang diagnostic pop") +#pragma mark - Clang Warnings + +// warning list ref:https://clang.llvm.org/docs/DiagnosticsReference.html +#define HIPPY_CLANG_WARN_CONCAT(warning_name) HIPPY_STR_EXPAND(clang diagnostic ignored warning_name) +#define HIPPY_IGNORE_WARNING_BEGIN(warningName) _Pragma("clang diagnostic push") _Pragma(HIPPY_CLANG_WARN_CONCAT(#warningName)) +#define HIPPY_IGNORE_WARNING_END _Pragma("clang diagnostic pop") + + +#pragma mark - + #define HIPPY_VERSION_3_0 300 diff --git a/modules/ios/base/HippyUtils.m b/modules/ios/base/HippyUtils.m index e257b5a0cc7..448fea984c1 100644 --- a/modules/ios/base/HippyUtils.m +++ b/modules/ios/base/HippyUtils.m @@ -438,15 +438,7 @@ static void HPGetRGBAColorComponents(CGColorRef color, CGFloat rgba[4]) { if (nil == uriData) { return nil; } - CFURLRef urlRef = NULL; - if ([URLString hasPrefix:@"http"] || - [URLString hasPrefix:@"data:"] || - [URLString hasPrefix:@"file:"]) { - urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, (__bridge CFURLRef)baseURL); - } - else { - urlRef = CFURLCreateWithFileSystemPath(NULL, (__bridge CFStringRef)URLString, kCFURLPOSIXPathStyle, NO); - } + CFURLRef urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, (__bridge CFURLRef)baseURL); NSURL *source_url = CFBridgingRelease(urlRef); return source_url; } diff --git a/modules/vfs/ios/VFSUriLoader.mm b/modules/vfs/ios/VFSUriLoader.mm index 0825443c4ba..d4c6ccda1c2 100644 --- a/modules/vfs/ios/VFSUriLoader.mm +++ b/modules/vfs/ios/VFSUriLoader.mm @@ -25,10 +25,9 @@ #import "TypeConverter.h" #import "VFSUriLoader.h" #import "VFSUriHandler.h" - +#import "HippyAssert.h" #include #include - #include "footstone/string_view_utils.h" NSString *const VFSErrorDomain = @"VFSErrorDomain"; @@ -91,7 +90,9 @@ NSOperationQueue *operationQueue, VFSHandlerProgressBlock progress, VFSHandlerCompletionBlock completion) { - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]; + NSURL *url = HippyURLWithString(urlString, nil); + HippyAssert(url, @"Invalid URL! %@", urlString); + NSURLRequest *request = [NSURLRequest requestWithURL:url]; RequestUntrustedContent(request, extraInfo, operationQueue, progress, completion); } diff --git a/tests/ios/HippyUtilsTest.m b/tests/ios/HippyUtilsTest.m new file mode 100644 index 00000000000..fb41d388923 --- /dev/null +++ b/tests/ios/HippyUtilsTest.m @@ -0,0 +1,90 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +@interface HippyUtilsTest : XCTestCase + +@end + +@implementation HippyUtilsTest + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testSDKVersionExists { + NSString *ver = [HippyUtils sdkVersion]; + XCTAssertNotNil(ver); +} + +- (void)testHippyURLWithString { + HIPPY_IGNORE_WARNING_BEGIN(-Wnonnull) + XCTAssertNil(HippyURLWithString(nil, nil)); + HIPPY_IGNORE_WARNING_END + XCTAssert([[HippyURLWithString(@"", nil) absoluteString] length] == 0); + + NSArray *testPaths = @[ + @"http://hippyjs.org", + @"https://hippyjs.org", + @"file:///testAbsulotePath/subPath", + @"hpfile://./testHippyRelativePath/subPath", + @"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAA", + // Some exceptions, such as Spaces or newlines + @"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAA\n\n ", + ]; + for (NSString *path in testPaths) { + NSURL *url = HippyURLWithString(path, nil); + XCTAssertNotNil(url); + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + XCTAssertNotNil(components.scheme); + XCTAssertNotNil(components.path); + } + testPaths = @[ + @"测试中文", + ]; + for (NSString *path in testPaths) { + NSURL *url = HippyURLWithString(path, nil); + XCTAssertNotNil(url); + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + XCTAssertNil(components.scheme); + XCTAssertNotNil(components.path); + } + + NSString *baseUrl = @"https://hippyjs.org/#/"; + testPaths = @[ + @"hello/hippy", + ]; + for (NSString *path in testPaths) { + NSURL *url = HippyURLWithString(path, baseUrl); + XCTAssert([url.absoluteString isEqualToString:@"https://hippyjs.org/hello/hippy"]); + } + +} + + +@end From f6458d1455335b792a1c0c7c50d702c61d715f80 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Mon, 22 Apr 2024 22:01:00 +0800 Subject: [PATCH 09/28] feat(ios): unify HippyBridge's notifications and improve compatibility --- .../RenderPage/HippyDemoViewController.mm | 4 +- .../examples/ios-demo/HippyDemo/TestModule.mm | 4 +- framework/ios/base/bridge/HippyBridge.h | 101 +++++++++++++++-- framework/ios/base/bridge/HippyBridge.mm | 106 ++++++++++++++---- renderer/native/ios/renderer/HippyRootView.h | 5 - renderer/native/ios/renderer/HippyRootView.mm | 22 +--- 6 files changed, 187 insertions(+), 55 deletions(-) diff --git a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm index 43481a05867..4b4ac52033e 100644 --- a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm +++ b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm @@ -198,7 +198,9 @@ - (void)mountConnector:(HippyBridge *)hippyBridge { } else { NSURL *vendorBundleURL = [self vendorBundleURL]; NSURL *indexBundleURL = [self indexBundleURL]; - [hippyBridge loadBundleURL:vendorBundleURL completion:^(NSURL * _Nullable, NSError * _Nullable) { + [hippyBridge loadBundleURL:vendorBundleURL + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable, NSError * _Nullable) { NSLog(@"url %@ load finish", vendorBundleURL); }]; hippyBridge.sandboxDirectory = [indexBundleURL URLByDeletingLastPathComponent]; diff --git a/framework/examples/ios-demo/HippyDemo/TestModule.mm b/framework/examples/ios-demo/HippyDemo/TestModule.mm index 04879102526..69a0e711364 100644 --- a/framework/examples/ios-demo/HippyDemo/TestModule.mm +++ b/framework/examples/ios-demo/HippyDemo/TestModule.mm @@ -95,7 +95,9 @@ - (void)mountConnector:(HippyBridge *)connector onView:(UIView *)view { rootView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; [_connector setRootView:rootView]; NSNumber *rootTag = [rootView hippyTag]; - [connector loadBundleURL:bundleUrl completion:^(NSURL * _Nullable, NSError * _Nullable) { + [connector loadBundleURL:bundleUrl + bundleType:HippyBridgeBundleTypeBusiness + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) { NSLog(@"url %@ load finish", bundleStr); [connector loadInstanceForRootView:rootTag withProperties:@{@"isSimulator": @(isSimulator)}]; }]; diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index 4b5b9e18212..d5697ad44db 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -42,20 +42,88 @@ NS_ASSUME_NONNULL_BEGIN * 注意:为兼容2.0版本,保持的相同的下划线前缀命名,不可修改 */ HIPPY_EXTERN NSString *const _HippySDKVersion; + /** * This notification triggers a reload of all bridges currently running. * Deprecated, use HippyBridge::requestReload instead. */ HIPPY_EXTERN NSString *const HippyReloadNotification; + +// Keys of userInfo for the following notifications +HIPPY_EXTERN NSString *const kHippyNotiBridgeKey; +HIPPY_EXTERN NSString *const kHippyNotiBundleUrlKey; +HIPPY_EXTERN NSString *const kHippyNotiBundleTypeKey; +HIPPY_EXTERN NSString *const kHippyNotiErrorKey; + +/// Bundle Type of Vendor (or Common Bundle), +/// used in kHippyNotiBundleTypeKey +HIPPY_EXTERN const NSUInteger HippyBridgeBundleTypeVendor; +/// Bundle Type Business, +/// used in kHippyNotiBundleTypeKey +HIPPY_EXTERN const NSUInteger HippyBridgeBundleTypeBusiness; + +/** + * This notification fires when the bridge starts loading and executing the JS bundle. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * } + * + * 备注:bundle包开始加载的通知, 注意与Hippy2不同的是,不仅指代`Common包`,`Business包`同样会发送该通知, + * 可通过userInfo中bundleType参数进行区分,see: HippyBridgeBundleTypeVendor + */ +HIPPY_EXTERN NSString *const HippyJavaScriptWillStartLoadingNotification; + +/** + * This notification fires when bridge has fetched JS bundle's source code. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * kHippyNotiErrorKey : $(error), // NSError object + * } + * + * 备注:获取到Bundle包的source code data时的通知 + */ +HIPPY_EXTERN NSString *const HippyJavaScripDidLoadSourceCodeNotification; + /** * This notification fires when the bridge has finished loading the JS bundle. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * } + * + * 备注:Bundle包`加载和执行`结束的通知 */ HIPPY_EXTERN NSString *const HippyJavaScriptDidLoadNotification; /** * This notification fires when the bridge failed to load the JS bundle. The * `error` key can be used to determine the error that occured. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * kHippyNotiErrorKey : $(error), // NSError object + * } + * + * 备注:Bundle包`加载和执行`失败的通知 */ HIPPY_EXTERN NSString *const HippyJavaScriptDidFailToLoadNotification; @@ -74,6 +142,8 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); +#pragma mark - + /// Async bridge used to communicate with the JavaScript application. @interface HippyBridge : NSObject @@ -143,14 +213,6 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); */ @property (nonatomic, strong, readonly) NSURL *debugURL; -/** - * Load js bundles from urls - * - * @param bundleURL bundles url - * @discussion HippyBridge makes sure bundles will be loaded in order. - */ -- (void)loadBundleURL:(NSURL *)bundleURL - completion:(void (^_Nullable)(NSURL * _Nullable, NSError * _Nullable))completion; #pragma mark - Image Related @@ -338,6 +400,29 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); - (void)setOSNightMode:(BOOL)isOSNightMode withRootViewTag:(NSNumber *)rootViewTag; + +#pragma mark - Advanced Usages + +/* 说明: + * 以下方法一般情况下无需调用,仅供高级定制化使用。 + * Following methods are only used for advanced customization, no need to be invoked in general. + */ + +typedef NSUInteger HippyBridgeBundleType; +typedef void (^HippyBridgeBundleLoadCompletionBlock)(NSURL * _Nullable bundleURL, NSError * _Nullable error); + +/// Load and Execute bundle from the given bundle URL +/// - Parameters: +/// - bundleURL: bundle url +/// - bundleType: type of bundle, e.g.: whether is `Vendor Bundle`(Common Bundle) or `Business Bundle` +/// - completion: Completion block +/// +/// - Disscusion: HippyBridge makes sure bundles will be loaded and execute in order. +- (void)loadBundleURL:(NSURL *)bundleURL + bundleType:(HippyBridgeBundleType)bundleType + completion:(HippyBridgeBundleLoadCompletionBlock)completion; + + @end diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index 815e7208f1b..7b6afcc8449 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -79,11 +79,22 @@ #include "devtools/devtools_data_source.h" #endif + +NSString *const _HippySDKVersion = @HIPPY_STR(HIPPY_VERSION); NSString *const HippyReloadNotification = @"HippyReloadNotification"; +NSString *const HippyJavaScriptWillStartLoadingNotification = @"HippyJavaScriptWillStartLoadingNotification"; +NSString *const HippyJavaScripDidLoadSourceCodeNotification = @"HippyJavaScripDidLoadSourceCodeNotification"; NSString *const HippyJavaScriptDidLoadNotification = @"HippyJavaScriptDidLoadNotification"; NSString *const HippyJavaScriptDidFailToLoadNotification = @"HippyJavaScriptDidFailToLoadNotification"; NSString *const HippyDidInitializeModuleNotification = @"HippyDidInitializeModuleNotification"; -NSString *const _HippySDKVersion = @HIPPY_STR(HIPPY_VERSION); + +NSString *const kHippyNotiBridgeKey = @"bridge"; +NSString *const kHippyNotiBundleUrlKey = @"bundleURL"; +NSString *const kHippyNotiBundleTypeKey = @"bundleType"; +NSString *const kHippyNotiErrorKey = @"error"; + +const NSUInteger HippyBridgeBundleTypeVendor = 1; +const NSUInteger HippyBridgeBundleTypeBusiness = 2; static NSString *const HippyNativeGlobalKeyOS = @"OS"; @@ -481,7 +492,7 @@ - (void)setUp { _javaScriptExecutor.contextName = _contextName; } _displayLink = [[HippyDisplayLink alloc] init]; - + // Setup all extra and internal modules [_moduleSetup setupModulesWithCompletionBlock:^{ HippyBridge *strongSelf = weakSelf; @@ -503,36 +514,61 @@ - (void)setUp { /// 加载初始化bridge时传入的Bundle URL - (void)loadPendingVendorBundleURLIfNeeded { if (self.pendingLoadingVendorBundleURL) { - [self loadBundleURL:self.pendingLoadingVendorBundleURL completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + [self loadBundleURL:self.pendingLoadingVendorBundleURL + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) { if (error) { - HippyLogError(@"[Hippy_OC_Log][HippyBridge], bundle loaded error:%@, %@", url, error.description); + HippyLogError(@"[Hippy_OC_Log][HippyBridge], bundle loaded error:%@, %@", bundleURL, error.description); } else { - HippyLogInfo(@"[Hippy_OC_Log][HippyBridge], bundle loaded success:%@", url); + HippyLogInfo(@"[Hippy_OC_Log][HippyBridge], bundle loaded success:%@", bundleURL); } }]; } } +#define BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO \ + @{ kHippyNotiBridgeKey: strongSelf, \ + kHippyNotiBundleUrlKey: bundleURL, \ + kHippyNotiBundleTypeKey : @(bundleType) } + +#define BUNDLE_LOAD_NOTI_ERROR_USER_INFO \ + @{ kHippyNotiBridgeKey: strongSelf, \ + kHippyNotiBundleUrlKey: bundleURL, \ + kHippyNotiBundleTypeKey : @(bundleType), \ + kHippyNotiErrorKey : error } - (void)loadBundleURL:(NSURL *)bundleURL - completion:(void (^_Nullable)(NSURL * _Nullable, NSError * _Nullable))completion { + bundleType:(HippyBridgeBundleType)bundleType + completion:(nonnull HippyBridgeBundleLoadCompletionBlock)completion { if (!bundleURL) { if (completion) { static NSString *bundleError = @"bundle url is nil"; - NSError *error = [NSError errorWithDomain:@"Bridge Bundle Loading Domain" code:1 userInfo:@{NSLocalizedFailureReasonErrorKey: bundleError}]; + NSError *error = [NSError errorWithDomain:@"Bridge Bundle Loading Domain" + code:1 + userInfo:@{NSLocalizedFailureReasonErrorKey: bundleError}]; completion(nil, error); } return; } - HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); + HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", + HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), + HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); [_bundleURLs addObject:bundleURL]; + + __weak __typeof(self)weakSelf = self; dispatch_async(HippyBridgeQueue(), ^{ - [self beginLoadingBundle:bundleURL completion:completion]; + __strong __typeof(weakSelf)strongSelf = weakSelf; + NSDictionary *userInfo = BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO; + [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScriptWillStartLoadingNotification + object:strongSelf + userInfo:userInfo]; + [strongSelf beginLoadingBundle:bundleURL bundleType:bundleType completion:completion]; }); } - (void)beginLoadingBundle:(NSURL *)bundleURL - completion:(void (^)(NSURL * _Nullable, NSError * _Nullable))completion { + bundleType:(HippyBridgeBundleType)bundleType + completion:(HippyBridgeBundleLoadCompletionBlock)completion { dispatch_group_t group = dispatch_group_create(); __weak HippyBridge *weakSelf = self; __block NSData *script = nil; @@ -545,23 +581,35 @@ - (void)beginLoadingBundle:(NSURL *)bundleURL bundleURL:bundleURL queue:bundleQueue]; fetchOp.onLoad = ^(NSData *source, NSError *error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if (!strongSelf) { + dispatch_group_leave(group); + return; + } + NSDictionary *userInfo; if (error) { HippyBridgeFatal(error, weakSelf); + userInfo = BUNDLE_LOAD_NOTI_ERROR_USER_INFO; } else { script = source; + userInfo = BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO; } + [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScripDidLoadSourceCodeNotification + object:strongSelf + userInfo:userInfo]; dispatch_group_leave(group); }; dispatch_group_enter(group); HippyBundleExecutionOperation *executeOp = [[HippyBundleExecutionOperation alloc] initWithBlock:^{ - HippyBridge *strongSelf = weakSelf; + __strong __typeof(weakSelf)strongSelf = weakSelf; if (!strongSelf || !strongSelf.valid) { dispatch_group_leave(group); return; } __weak __typeof(strongSelf)weakSelf = strongSelf; [strongSelf executeJSCode:script sourceURL:bundleURL onCompletion:^(id result, NSError *error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; HippyLogInfo(@"End loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); @@ -569,7 +617,6 @@ - (void)beginLoadingBundle:(NSURL *)bundleURL if (completion) { completion(bundleURL, error); } - HippyBridge *strongSelf = weakSelf; if (!strongSelf || !strongSelf.valid) { dispatch_group_leave(group); return; @@ -577,6 +624,25 @@ - (void)beginLoadingBundle:(NSURL *)bundleURL if (error) { HippyBridgeFatal(error, strongSelf); } + __weak __typeof(self)weakSelf = strongSelf; + dispatch_async(dispatch_get_main_queue(), ^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSNotificationName notiName; + NSDictionary *userInfo; + if (error) { + notiName = HippyJavaScriptDidFailToLoadNotification; + userInfo = BUNDLE_LOAD_NOTI_ERROR_USER_INFO; + } else { + notiName = HippyJavaScriptDidLoadNotification; + userInfo = BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO; + } + [[NSNotificationCenter defaultCenter] postNotificationName:notiName + object:strongSelf + userInfo:userInfo]; + }); dispatch_group_leave(group); }]; } queue:bundleQueue]; @@ -660,6 +726,10 @@ - (void)setInspectable:(BOOL)isInspectable { [self.javaScriptExecutor setInspecable:isInspectable]; } + +#pragma mark - Private + +/// Execute JS Bundle - (void)executeJSCode:(NSData *)script sourceURL:(NSURL *)sourceURL onCompletion:(HippyJavaScriptCallback)completion { @@ -682,14 +752,6 @@ - (void)executeJSCode:(NSData *)script if (error) { [strongSelf stopLoadingWithError:error scriptSourceURL:sourceURL]; } - else { - dispatch_async(dispatch_get_main_queue(), ^{ - NSDictionary *userInfo = @{@"bridge": self, sourceURL: sourceURL}; - [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScriptDidLoadNotification - object:self - userInfo:userInfo]; - }); - } completion(result, error); }]; } @@ -708,10 +770,6 @@ - (void)stopLoadingWithError:(NSError *)error scriptSourceURL:(NSURL *)sourceURL } } }]; - NSDictionary *userInfo = @{@"bridge": self, @"error": error, @"sourceURL": sourceURL}; - [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScriptDidFailToLoadNotification - object:self - userInfo:userInfo]; if ([error userInfo][HippyJSStackTraceKey]) { [self.redBox showErrorMessage:[error localizedDescription] withStack:[error userInfo][HippyJSStackTraceKey]]; } diff --git a/renderer/native/ios/renderer/HippyRootView.h b/renderer/native/ios/renderer/HippyRootView.h index 33a8aba604d..fa56135a43c 100644 --- a/renderer/native/ios/renderer/HippyRootView.h +++ b/renderer/native/ios/renderer/HippyRootView.h @@ -47,11 +47,6 @@ typedef NS_ENUM(NSInteger, HippyRootViewSizeFlexibility) { /// is a good indicator that the application is ready to use. extern NSString *const HippyContentDidAppearNotification; -/// Business bundle loading completion notification -/// This notification is for compatibility with hippy2 and is not recommended for further use -extern NSString *const HippySecondaryBundleDidLoadNotification; - - /// Native view used to host Hippy-managed views within the app. /// Can be used just like any ordinary UIView. diff --git a/renderer/native/ios/renderer/HippyRootView.mm b/renderer/native/ios/renderer/HippyRootView.mm index f9ed9123ed5..e8ca67e0b91 100644 --- a/renderer/native/ios/renderer/HippyRootView.mm +++ b/renderer/native/ios/renderer/HippyRootView.mm @@ -35,11 +35,6 @@ // Sent when the first subviews are added to the root view NSString *const HippyContentDidAppearNotification = @"HippyContentDidAppearNotification"; -// In hippy2 there are two concepts: common package and business package; -// After the success of the business package loading will send a `SecondaryBundleDidLoad` notification; -// For compatibility, hippy3 retains this notice and its actual meaning. -NSString *const HippySecondaryBundleDidLoadNotification = @"HippySecondaryBundleDidLoadNotification"; - NSNumber *AllocRootViewTag(void) { static NSString * const token = @"allocateRootTag"; @synchronized (token) { @@ -156,7 +151,9 @@ - (instancetype)initWithBridge:(HippyBridge *)bridge } } else { __weak __typeof(self)weakSelf = self; - [bridge loadBundleURL:businessURL completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + [bridge loadBundleURL:businessURL + bundleType:HippyBridgeBundleTypeBusiness + completion:^(NSURL * _Nullable url, NSError * _Nullable error) { // Execute loadInstance first and then do call back, maintain compatibility with hippy2 dispatch_async(dispatch_get_main_queue(), ^{ __strong __typeof(weakSelf)strongSelf = weakSelf; @@ -166,13 +163,6 @@ - (instancetype)initWithBridge:(HippyBridge *)bridge if (!error && !strongSelf.disableAutoRunApplication) { [strongSelf runHippyApplication]; } - // 抛出业务包(BusinessBundle aka SecondaryBundle)加载完成通知, for hippy2兼容 - NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{ @"url": url, - @"bridge": strongSelf.bridge }]; - if (error) [userInfo setObject:error forKey:@"error"]; - [[NSNotificationCenter defaultCenter] postNotificationName:HippySecondaryBundleDidLoadNotification - object:strongSelf.bridge userInfo:userInfo]; - if ([delegate respondsToSelector:@selector(rootView:didLoadFinish:)]) { [delegate rootView:strongSelf didLoadFinish:(error == nil)]; } @@ -285,15 +275,15 @@ - (void)javaScriptDidLoad:(NSNotification *)notification { // Use the bridge that's sent in the notification payload // Call runHippyApplication only if the RootView is initialized without a business bundle. - HippyBridge *bridge = notification.userInfo[@"bridge"]; + HippyBridge *bridge = notification.userInfo[kHippyNotiBridgeKey]; if (!self.disableAutoRunApplication && bridge == self.bridge && !_hasBusinessBundleToLoad) { [self runHippyApplication]; } } - (void)javaScriptDidFailToLoad:(NSNotification *)notification { - HippyBridge *bridge = notification.userInfo[@"bridge"]; - NSError *error = notification.userInfo[@"error"]; + HippyBridge *bridge = notification.userInfo[kHippyNotiBridgeKey]; + NSError *error = notification.userInfo[kHippyNotiErrorKey]; if (bridge == self.bridge && error) { NSError *retError = HippyErrorFromErrorAndModuleName(error, self.bridge.moduleName); HippyFatal(retError); From e4d2df827d074fe0d1d328fb24ba3b370da56480 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 23 Apr 2024 15:56:52 +0800 Subject: [PATCH 10/28] chore(ios): add SecondaryBundleDidLoadNotification for compatibility --- renderer/native/ios/renderer/HippyRootView.h | 4 ++++ renderer/native/ios/renderer/HippyRootView.mm | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/renderer/native/ios/renderer/HippyRootView.h b/renderer/native/ios/renderer/HippyRootView.h index fa56135a43c..8860247c635 100644 --- a/renderer/native/ios/renderer/HippyRootView.h +++ b/renderer/native/ios/renderer/HippyRootView.h @@ -47,6 +47,10 @@ typedef NS_ENUM(NSInteger, HippyRootViewSizeFlexibility) { /// is a good indicator that the application is ready to use. extern NSString *const HippyContentDidAppearNotification; +/// Business bundle loading completion notification +/// This notification is for compatibility with hippy2 and is not recommended for further use +extern NSString *const HippySecondaryBundleDidLoadNotification DEPRECATED_MSG_ATTRIBUTE("use HippyJavaScriptDidLoadNotification"); + /// Native view used to host Hippy-managed views within the app. /// Can be used just like any ordinary UIView. diff --git a/renderer/native/ios/renderer/HippyRootView.mm b/renderer/native/ios/renderer/HippyRootView.mm index e8ca67e0b91..b99d9553c45 100644 --- a/renderer/native/ios/renderer/HippyRootView.mm +++ b/renderer/native/ios/renderer/HippyRootView.mm @@ -35,6 +35,12 @@ // Sent when the first subviews are added to the root view NSString *const HippyContentDidAppearNotification = @"HippyContentDidAppearNotification"; +// In hippy2 there are two concepts: common package and business package; +// After the success of the business package loading will send a `SecondaryBundleDidLoad` notification; +// For compatibility, hippy3 retains this notice and its actual meaning. +NSString *const HippySecondaryBundleDidLoadNotification = @"HippySecondaryBundleDidLoadNotification"; + + NSNumber *AllocRootViewTag(void) { static NSString * const token = @"allocateRootTag"; @synchronized (token) { @@ -163,6 +169,17 @@ - (instancetype)initWithBridge:(HippyBridge *)bridge if (!error && !strongSelf.disableAutoRunApplication) { [strongSelf runHippyApplication]; } + + // 抛出业务包(BusinessBundle aka SecondaryBundle)加载完成通知,for hippy2兼容 + NSMutableDictionary *userInfo = @{ kHippyNotiBundleUrlKey: url, + kHippyNotiBridgeKey: strongSelf.bridge }.mutableCopy; + if (error) { [userInfo setObject:error forKey:kHippyNotiErrorKey]; } + HIPPY_IGNORE_WARNING_BEGIN(-Wdeprecated) + [[NSNotificationCenter defaultCenter] postNotificationName:HippySecondaryBundleDidLoadNotification + object:strongSelf.bridge + userInfo:userInfo]; + HIPPY_IGNORE_WARNING_END + if ([delegate respondsToSelector:@selector(rootView:didLoadFinish:)]) { [delegate rootView:strongSelf didLoadFinish:(error == nil)]; } From b1dea840273d4e44fac656e3f570f6f92c16debb Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 23 Apr 2024 17:34:47 +0800 Subject: [PATCH 11/28] chore(ios): optimize parameter check tips --- framework/ios/base/modules/HippyModuleMethod.mm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/framework/ios/base/modules/HippyModuleMethod.mm b/framework/ios/base/modules/HippyModuleMethod.mm index 65bf27b0df2..2a2ea61d786 100644 --- a/framework/ios/base/modules/HippyModuleMethod.mm +++ b/framework/ios/base/modules/HippyModuleMethod.mm @@ -480,9 +480,15 @@ - (id)invokeWithBridge:(HippyBridge *)bridge module:(id)module arguments:(NSArra NSInteger actualCount = arguments.count; NSInteger expectedCount = _argumentBlocks.count; BOOL isArgumentsMismatch = NO; - if (actualCount > expectedCount || - (self.functionType == HippyFunctionTypePromise && actualCount < expectedCount - 2)) { + if (actualCount > expectedCount) { isArgumentsMismatch = YES; + } else if (self.functionType == HippyFunctionTypePromise && actualCount < expectedCount - 2) { + for (NSInteger index = actualCount; index < expectedCount - 2; index++) { + id arg = self.arguments[index]; + if (arg.nullability != HippyNullable) { + isArgumentsMismatch = YES; + } + } } if (isArgumentsMismatch) { HippyLogError(@"%@.%@ was called with %lld arguments but expects %lld arguments. " @@ -499,8 +505,7 @@ - (id)invokeWithBridge:(HippyBridge *)bridge module:(id)module arguments:(NSArra // Set arguments NSUInteger index = 0; for (id json in arguments) { - // release模式下,如果前端给的参数多于终端所需参数,那会造成数组越界,引起整个逻辑return。 - //这里做个修改,如果前端给的参数过多,那忽略多余的参数。 + // 如果前端给的参数过多,忽略多余的参数 if ([_argumentBlocks count] <= index) { break; } From 2fdf1b2126f1a67ea419501d053ae33ff1e0d12b Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 23 Apr 2024 22:51:29 +0800 Subject: [PATCH 12/28] fix(ios): move nodes op non same parent support --- .../native/ios/renderer/HippyUIManager.mm | 24 +++++++++---------- .../ios/renderer/NativeRenderManager.mm | 21 +++++++++++++++- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index a4c832ccbe4..207ea5a3f1c 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -958,20 +958,18 @@ - (void)renderMoveViews:(const std::vector &&)ids } int32_t rootTag = strongRootNode->GetId(); - HippyShadowView *fromObjectView = [_shadowViewRegistry componentForTag:@(fromContainer) - onRootTag:@(rootTag)]; - HippyShadowView *toObjectView = [_shadowViewRegistry componentForTag:@(toContainer) - onRootTag:@(rootTag)]; - for (int32_t componentTag : ids) { - HippyShadowView *view = [_shadowViewRegistry componentForTag:@(componentTag) onRootTag:@(rootTag)]; - HippyAssert(fromObjectView == [view parent], @"parent of object view with tag %d is not object view with tag %d", componentTag, fromContainer); + HippyShadowView *fromShadowView = [_shadowViewRegistry componentForTag:@(fromContainer) onRootTag:@(rootTag)]; + HippyShadowView *toShadowView = [_shadowViewRegistry componentForTag:@(toContainer) onRootTag:@(rootTag)]; + for (int32_t hippyTag : ids) { + HippyShadowView *view = [_shadowViewRegistry componentForTag:@(hippyTag) onRootTag:@(rootTag)]; + HippyAssert(fromShadowView == [view parent], @"ShadowView(%d)'s parent should be %d", hippyTag, fromContainer); [view removeFromHippySuperview]; - [toObjectView insertHippySubview:view atIndex:index]; + [toShadowView insertHippySubview:view atIndex:index]; } - [fromObjectView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; - [toObjectView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; - [fromObjectView didUpdateHippySubviews]; - [toObjectView didUpdateHippySubviews]; + [fromShadowView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; + [toShadowView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; + [fromShadowView didUpdateHippySubviews]; + [toShadowView didUpdateHippySubviews]; auto strongTags = std::move(ids); [self addUIBlock:^(__unused HippyUIManager *uiManager, NSDictionary *viewRegistry) { UIView *fromView = [viewRegistry objectForKey:@(fromContainer)]; @@ -1006,7 +1004,7 @@ - (void)renderMoveNodes:(std::vector> &&)nodes int32_t componentTag = node->GetId(); HippyShadowView *objectView = [_shadowViewRegistry componentForTag:@(componentTag) onRootTag:@(rootTag)]; [objectView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; - HippyAssert(!parentObjectView || parentObjectView == [objectView parent], @"try to move object view on different parent object view"); + HippyAssert(!parentObjectView || parentObjectView == [objectView parent], @"parent not same!"); if (!parentObjectView) { parentObjectView = (HippyShadowView *)[objectView parent]; } diff --git a/renderer/native/ios/renderer/NativeRenderManager.mm b/renderer/native/ios/renderer/NativeRenderManager.mm index 463786279c7..17d3e5eb5c5 100644 --- a/renderer/native/ios/renderer/NativeRenderManager.mm +++ b/renderer/native/ios/renderer/NativeRenderManager.mm @@ -107,7 +107,26 @@ std::vector>&& nodes) { @autoreleasepool { HippyAssert(renderImpl_, @"renderImpl_ is null, did you forget to call Initialize()?"); - [renderImpl_ renderMoveNodes:std::move(nodes) onRootNode:root_node]; + // Check whether all nodes have the same pid + uint32_t firstPid = nodes[0]->GetPid(); + bool allSamePid = std::all_of(nodes.begin(), nodes.end(), + [firstPid](const std::shared_ptr& node) { + return node->GetPid() == firstPid; + }); + + if (allSamePid) { + // If all nodes have the same pid, call directly + [renderImpl_ renderMoveNodes:std::move(nodes) onRootNode:root_node]; + } else { + // If not, group them by pid and then call for each group + std::map>> pidNodeMap; + for (auto& node : nodes) { + pidNodeMap[node->GetPid()].push_back(node); + } + for (auto& pair : pidNodeMap) { + [renderImpl_ renderMoveNodes:std::move(pair.second) onRootNode:root_node]; + } + } } } From c63f221eb806d0823a160494575b5d45a30fe0b6 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 24 Apr 2024 17:41:01 +0800 Subject: [PATCH 13/28] fix(ios): fix move node index bug --- renderer/native/ios/renderer/HippyUIManager.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index 207ea5a3f1c..c5bdcf7d049 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -1015,7 +1015,7 @@ - (void)renderMoveNodes:(std::vector> &&)nodes [self addUIBlock:^(__unused HippyUIManager *uiManager, NSDictionary *viewRegistry) { UIView *superView = nil; for (auto node : strongNodes) { - int32_t index = node->GetIndex(); + int32_t index = node->GetRenderInfo().index; int32_t componentTag = node->GetId(); UIView *view = [viewRegistry objectForKey:@(componentTag)]; if (!view) { From 7910930318933dd08a4efb86e9f5054a3ee3278e Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 24 Apr 2024 18:40:18 +0800 Subject: [PATCH 14/28] fix(ios): debug reload module not working --- .../RenderPage/HippyDemoViewController.mm | 4 - framework/ios/base/HippyKeyCommands.h | 4 +- framework/ios/base/HippyKeyCommands.m | 270 ++++++++---------- framework/ios/base/bridge/HippyBridge.mm | 6 +- .../ios/base/bridge/HippyBridgeDelegate.h | 8 - framework/ios/module/dev/HippyDevMenu.h | 5 - framework/ios/module/dev/HippyDevMenu.mm | 61 ++-- modules/ios/base/HippyUtils.h | 1 + modules/ios/base/HippyUtils.m | 12 + 9 files changed, 156 insertions(+), 215 deletions(-) diff --git a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm index 4b4ac52033e..e4bb6087b5e 100644 --- a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm +++ b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm @@ -263,10 +263,6 @@ - (BOOL)isDebugMode { return _isDebugMode; } -- (void)reload:(HippyBridge *)bridge { - [self mountConnector:_hippyBridge]; -} - - (void)removeRootView:(NSNumber *)rootTag bridge:(HippyBridge *)bridge { [[[self.contentAreaView subviews] firstObject] removeFromSuperview]; } diff --git a/framework/ios/base/HippyKeyCommands.h b/framework/ios/base/HippyKeyCommands.h index 1ccdd4b6f39..1ca74362dec 100644 --- a/framework/ios/base/HippyKeyCommands.h +++ b/framework/ios/base/HippyKeyCommands.h @@ -29,7 +29,9 @@ /** * Register a single-press keyboard command. */ -- (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *command))block; +- (void)registerKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *command))block; /** * Unregister a single-press keyboard command. diff --git a/framework/ios/base/HippyKeyCommands.m b/framework/ios/base/HippyKeyCommands.m index e7a33ee1f17..728999066c1 100644 --- a/framework/ios/base/HippyKeyCommands.m +++ b/framework/ios/base/HippyKeyCommands.m @@ -25,12 +25,21 @@ #import "HippyAssert.h" #import "HippyDefines.h" #import "HippyUtils.h" +#import "HippyRootView.h" +#import +#import #if HIPPY_DEV -static BOOL HippyIsIOS8OrEarlier() { - return [UIDevice currentDevice].systemVersion.floatValue < 9; -} +@interface UIEvent (UIPhysicalKeyboardEvent) + +@property (nonatomic) NSString *_modifiedInput; +@property (nonatomic) NSString *_unmodifiedInput; +@property (nonatomic) UIKeyModifierFlags _modifierFlags; +@property (nonatomic) BOOL _isKeyDown; +@property (nonatomic) long _keyCode; + +@end @interface HippyKeyCommand : NSObject @@ -49,6 +58,8 @@ - (instancetype)initWithKeyCommand:(UIKeyCommand *)keyCommand block:(void (^)(UI return self; } +HIPPY_NOT_IMPLEMENTED(-(instancetype)init) + - (id)copyWithZone:(__unused NSZone *)zone { return self; } @@ -65,147 +76,114 @@ - (BOOL)isEqual:(HippyKeyCommand *)object { } - (BOOL)matchesInput:(NSString *)input flags:(UIKeyModifierFlags)flags { - return [_keyCommand.input isEqual:input] && _keyCommand.modifierFlags == flags; + return [_keyCommand.input isEqual:input] && (_keyCommand.modifierFlags == flags || flags == 0); } - (NSString *)description { - return [NSString stringWithFormat:@"<%@:%p input=\"%@\" flags=%zd hasBlock=%@>", [self class], self, _keyCommand.input, - (long)_keyCommand.modifierFlags, _block ? @"YES" : @"NO"]; + return [NSString stringWithFormat:@"<%@:%p input=\"%@\" flags=%zd hasBlock=%@>", + [self class], self, _keyCommand.input, (long)_keyCommand.modifierFlags, _block ? @"YES" : @"NO"]; } @end + +#pragma mark - + @interface HippyKeyCommands () @property (nonatomic, strong) NSMutableSet *commands; @end -@implementation UIResponder (HippyKeyCommands) - -+ (UIResponder *)hippy_getFirstResponder:(UIResponder *)view { - UIResponder *firstResponder = nil; - - if (view.isFirstResponder) { - return view; - } else if ([view isKindOfClass:[UIViewController class]]) { - if ([(UIViewController *)view parentViewController]) { - firstResponder = [UIResponder hippy_getFirstResponder:[(UIViewController *)view parentViewController]]; - } - return firstResponder ? firstResponder : [UIResponder hippy_getFirstResponder:[(UIViewController *)view view]]; - } else if ([view isKindOfClass:[UIView class]]) { - for (UIView *subview in [(UIView *)view subviews]) { - firstResponder = [UIResponder hippy_getFirstResponder:subview]; - if (firstResponder) { - return firstResponder; - } - } - } - return firstResponder; -} +@implementation HippyKeyCommands -- (NSArray *)hippy_keyCommands { - NSSet *commands = [HippyKeyCommands sharedInstance].commands; - return [[commands valueForKeyPath:@"keyCommand"] allObjects]; ++ (void)initialize { + SEL originalKeyEventSelector = NSSelectorFromString(@"handleKeyUIEvent:"); + SEL swizzledKeyEventSelector = NSSelectorFromString( + [NSString stringWithFormat:@"_hippy_swizzle_%x_%@", + arc4random(), NSStringFromSelector(originalKeyEventSelector)]); + + void (^handleKeyUIEventSwizzleBlock)(UIApplication *, UIEvent *) = ^(UIApplication *slf, UIEvent *event) { + [[[self class] sharedInstance] handleKeyUIEventSwizzle:event]; + ((void (*)(id, SEL, id))objc_msgSend)(slf, swizzledKeyEventSelector, event); + }; + + HippySwapInstanceMethodWithBlock([UIApplication class], originalKeyEventSelector, + handleKeyUIEventSwizzleBlock, swizzledKeyEventSelector); } -/** - * Single Press Key Command Response - * Command + KeyEvent (Command + R/D, etc.) - */ -- (void)hippy_handleKeyCommand:(UIKeyCommand *)key { - // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: - // method gets called repeatedly if the command key is held down. - static NSTimeInterval lastCommand = 0; - if (HippyIsIOS8OrEarlier() || CACurrentMediaTime() - lastCommand > 0.5) { - for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands.allObjects) { // add by stockGroup - if ([command.keyCommand.input isEqualToString:key.input] && command.keyCommand.modifierFlags == key.modifierFlags) { - if (command.block) { - command.block(key); - lastCommand = CACurrentMediaTime(); - } - } - } +- (void)handleKeyUIEventSwizzle:(UIEvent *)event { + NSString *modifiedInput = nil; + UIKeyModifierFlags modifierFlags = 0; + BOOL isKeyDown = NO; + + if ([event respondsToSelector:@selector(_modifiedInput)]) { + modifiedInput = [event _modifiedInput]; } -} - -/** - * Double Press Key Command Response - * Double KeyEvent (Double R, etc.) - */ -- (void)hippy_handleDoublePressKeyCommand:(UIKeyCommand *)key { - static BOOL firstPress = YES; - static NSTimeInterval lastCommand = 0; - static NSTimeInterval lastDoubleCommand = 0; - static NSString *lastInput = nil; - static UIKeyModifierFlags lastModifierFlags = 0; - - if (firstPress) { - for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands.allObjects) { // add by stockGroup - if ([command.keyCommand.input isEqualToString:key.input] && command.keyCommand.modifierFlags == key.modifierFlags && command.block) { - firstPress = NO; - lastCommand = CACurrentMediaTime(); - lastInput = key.input; - lastModifierFlags = key.modifierFlags; - return; + + if ([event respondsToSelector:@selector(_modifierFlags)]) { + modifierFlags = [event _modifierFlags]; + } + + if ([event respondsToSelector:@selector(_isKeyDown)]) { + isKeyDown = [event _isKeyDown]; + } + + BOOL interactionEnabled = !UIApplication.sharedApplication.isIgnoringInteractionEvents; + BOOL hasFirstResponder = NO; + if (isKeyDown && modifiedInput.length > 0 && interactionEnabled) { + UIResponder *firstResponder = nil; + for (UIWindow *window in [self allWindows]) { + firstResponder = [window valueForKey:@"firstResponder"]; + if (firstResponder) { + hasFirstResponder = YES; + break; } } - } else { - // Second keyevent within 0.2 second, - // with the same key as the first one. - if (CACurrentMediaTime() - lastCommand < 0.2 && lastInput == key.input && lastModifierFlags == key.modifierFlags) { - for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands.allObjects) { // add by stockGroup - if ([command.keyCommand.input isEqualToString:key.input] && command.keyCommand.modifierFlags == key.modifierFlags && command.block) { - // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: - // method gets called repeatedly if the command key is held down. - if (HippyIsIOS8OrEarlier() || CACurrentMediaTime() - lastDoubleCommand > 0.5) { - command.block(key); - lastDoubleCommand = CACurrentMediaTime(); - } - firstPress = YES; - return; - } - } + + // Ignore key commands (except escape) when there's an active responder + if (!firstResponder || [firstResponder isKindOfClass:HippyRootView.class]) { + [self hippy_handleKeyCommand:modifiedInput flags:modifierFlags]; } - - lastCommand = CACurrentMediaTime(); - lastInput = key.input; - lastModifierFlags = key.modifierFlags; } +}; + +- (NSArray *)allWindows { + BOOL includeInternalWindows = YES; + BOOL onlyVisibleWindows = NO; + + // Obfuscating selector allWindowsIncludingInternalWindows:onlyVisibleWindows: + NSArray *allWindowsComponents = + @[ @"al", @"lWindo", @"wsIncl", @"udingInt", @"ernalWin", @"dows:o", @"nlyVisi", @"bleWin", @"dows:" ]; + SEL allWindowsSelector = NSSelectorFromString([allWindowsComponents componentsJoinedByString:@""]); + + NSMethodSignature *methodSignature = [[UIWindow class] methodSignatureForSelector:allWindowsSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + + invocation.target = [UIWindow class]; + invocation.selector = allWindowsSelector; + [invocation setArgument:&includeInternalWindows atIndex:2]; + [invocation setArgument:&onlyVisibleWindows atIndex:3]; + [invocation invoke]; + + __unsafe_unretained NSArray *windows = nil; + [invocation getReturnValue:&windows]; + return windows; } -@end - -@implementation UIApplication (HippyKeyCommands) - -// Required for iOS 8.x -- (BOOL)hippy_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event { - if (action == @selector(hippy_handleKeyCommand:)) { - [self hippy_handleKeyCommand:sender]; - return YES; - } else if (action == @selector(hippy_handleDoublePressKeyCommand:)) { - [self hippy_handleDoublePressKeyCommand:sender]; - return YES; +- (void)hippy_handleKeyCommand:(NSString *)input flags:(UIKeyModifierFlags)modifierFlags { + for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands) { + if ([command matchesInput:input flags:modifierFlags]) { + if (command.block) { + command.block(nil); + } + } } - return [self hippy_sendAction:action to:target from:sender forEvent:event]; } -@end - -@implementation HippyKeyCommands -+ (void)initialize { - if (HippyIsIOS8OrEarlier()) { - // swizzle UIApplication - HippySwapInstanceMethods([UIApplication class], @selector(keyCommands), @selector(hippy_keyCommands)); - - HippySwapInstanceMethods([UIApplication class], @selector(sendAction:to:from:forEvent:), @selector(hippy_sendAction:to:from:forEvent:)); - } else { - // swizzle UIResponder - HippySwapInstanceMethods([UIResponder class], @selector(keyCommands), @selector(hippy_keyCommands)); - } -} +#pragma mark - + (instancetype)sharedInstance { static HippyKeyCommands *sharedInstance; @@ -213,7 +191,7 @@ + (instancetype)sharedInstance { dispatch_once(&onceToken, ^{ sharedInstance = [self new]; }); - + return sharedInstance; } @@ -224,19 +202,12 @@ - (instancetype)init { return self; } -- (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *))block { +- (void)registerKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *))block { HippyAssertMainQueue(); - - if (input.length && flags && HippyIsIOS8OrEarlier()) { - // Workaround around the first cmd not working: http://openradar.appspot.com/19613391 - // You can register just the cmd key and do nothing. This ensures that - // command-key modified commands will work first time. Fixed in iOS 9. - - [self registerKeyCommandWithInput:@"" modifierFlags:flags action:nil]; - } - - UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(hippy_handleKeyCommand:)]; - + + UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(description)]; HippyKeyCommand *keyCommand = [[HippyKeyCommand alloc] initWithKeyCommand:command block:block]; [_commands removeObject:keyCommand]; [_commands addObject:keyCommand]; @@ -244,7 +215,7 @@ - (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifi - (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { [_commands removeObject:command]; @@ -255,8 +226,8 @@ - (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModi - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - - for (HippyKeyCommand *command in _commands.allObjects) { // add by stockGroup + + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { return YES; } @@ -264,19 +235,12 @@ - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyMod return NO; } -- (void)registerDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *))block { +- (void)registerDoublePressKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *))block { HippyAssertMainQueue(); - - if (input.length && flags && HippyIsIOS8OrEarlier()) { - // Workaround around the first cmd not working: http://openradar.appspot.com/19613391 - // You can register just the cmd key and do nothing. This ensures that - // command-key modified commands will work first time. Fixed in iOS 9. - - [self registerDoublePressKeyCommandWithInput:@"" modifierFlags:flags action:nil]; - } - - UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(hippy_handleDoublePressKeyCommand:)]; - + + UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(description)]; HippyKeyCommand *keyCommand = [[HippyKeyCommand alloc] initWithKeyCommand:command block:block]; [_commands removeObject:keyCommand]; [_commands addObject:keyCommand]; @@ -284,7 +248,7 @@ - (void)registerDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:( - (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { [_commands removeObject:command]; @@ -295,8 +259,8 @@ - (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags - (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - - for (HippyKeyCommand *command in _commands.allObjects) { // add by stockGroup + + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { return YES; } @@ -319,10 +283,12 @@ - (void)registerKeyCommandWithInput:(__unused NSString *)input action:(__unused void (^)(UIKeyCommand *))block { } -- (void)unregisterKeyCommandWithInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (void)unregisterKeyCommandWithInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { } -- (BOOL)isKeyCommandRegisteredForInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (BOOL)isKeyCommandRegisteredForInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { return NO; } @@ -331,10 +297,12 @@ - (void)registerDoublePressKeyCommandWithInput:(__unused NSString *)input action:(__unused void (^)(UIKeyCommand *))block { } -- (void)unregisterDoublePressKeyCommandWithInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (void)unregisterDoublePressKeyCommandWithInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { } -- (BOOL)isDoublePressKeyCommandRegisteredForInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (BOOL)isDoublePressKeyCommandRegisteredForInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { return NO; } diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index 7b6afcc8449..ca43ee33668 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -449,13 +449,11 @@ - (void)addImageProviderClass:(Class)cls { #pragma mark - Debug Reload - (void)reload { - if ([self.delegate respondsToSelector:@selector(reload:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ self.invalidateReason = HippyInvalidateReasonReload; [self invalidate]; [self setUp]; - [self.delegate reload:self]; - self.invalidateReason = HippyInvalidateReasonDealloc; - } + }); } - (void)requestReload { diff --git a/framework/ios/base/bridge/HippyBridgeDelegate.h b/framework/ios/base/bridge/HippyBridgeDelegate.h index 2453facd476..b3e32225b54 100644 --- a/framework/ios/base/bridge/HippyBridgeDelegate.h +++ b/framework/ios/base/bridge/HippyBridgeDelegate.h @@ -55,14 +55,6 @@ */ - (void)cachedCodeCreated:(NSData *)cachedCode ForBridge:(HippyBridge *)bridge script:(NSString *)script sourceURL:(NSURL *)sourceURL; -//invalidate methods -/** - * Invoke when HippyBridge requests reloading - * - * @param bridge HippyBridge that requests reloading - */ -- (void)reload:(HippyBridge *)bridge; - /** * Tell delegate to remove root node * diff --git a/framework/ios/module/dev/HippyDevMenu.h b/framework/ios/module/dev/HippyDevMenu.h index 3ca48042d49..fbf3c34499c 100644 --- a/framework/ios/module/dev/HippyDevMenu.h +++ b/framework/ios/module/dev/HippyDevMenu.h @@ -69,11 +69,6 @@ */ - (void)reload; -/** - * Deprecated. Use the `-addItem:` method instead. - */ -- (void)addItem:(NSString *)title handler:(void (^)(void))handler DEPRECATED_ATTRIBUTE; - /** * Add custom item to the development menu. The handler will be called * when user selects the item. diff --git a/framework/ios/module/dev/HippyDevMenu.mm b/framework/ios/module/dev/HippyDevMenu.mm index b04786630a2..09369a570d6 100644 --- a/framework/ios/module/dev/HippyDevMenu.mm +++ b/framework/ios/module/dev/HippyDevMenu.mm @@ -109,6 +109,7 @@ - (void)callHandler { @interface HippyDevMenu () { __weak UIAlertController *_actionSheet; + NSMutableArray *_extraMenuItems; NSUserDefaults *_defaults; } @@ -142,38 +143,19 @@ - (instancetype)init { - (void)setBridge:(HippyBridge *)bridge { _bridge = bridge; -#if TARGET_IPHONE_SIMULATOR +#if TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST if (bridge.debugMode) { - __weak HippyDevMenu *weakSelf = self; - HippyKeyCommands *commands = [HippyKeyCommands sharedInstance]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"d" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - + __weak __typeof(self) weakSelf = self; + // Toggle debug menu - [commands registerKeyCommandWithInput:@"e" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"b" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"u" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"g" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { + [commands registerKeyCommandWithInput:@"d" + modifierFlags:UIKeyModifierCommand + action:^(__unused UIKeyCommand *command) { [weakSelf toggle]; }]; } -#endif +#endif /* TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST */ } - (dispatch_queue_t)methodQueue { @@ -181,8 +163,7 @@ - (dispatch_queue_t)methodQueue { } - (void)invalidate { - [_actionSheet dismissViewControllerAnimated:YES completion:^(void) { - }]; + [_actionSheet dismissViewControllerAnimated:YES completion:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -204,24 +185,20 @@ - (void)toggle { } } -- (void)addItem:(NSString *)title handler:(void (^)(void))handler { - [self addItem:[HippyDevMenuItem buttonItemWithTitle:title handler:handler]]; -} - -- (void)addItem:(__unused HippyDevMenuItem *)item { - HippyAssert(NO, @"[HippyDevMenu addItem:]方法没有实现,怎么没问题?"); +- (void)addItem:(HippyDevMenuItem *)item { + [_extraMenuItems addObject:item]; } - (NSArray *)menuItems { NSMutableArray *items = [NSMutableArray new]; // Add built-in items - __weak HippyDevMenu *weakSelf = self; - [items addObject:[HippyDevMenuItem buttonItemWithTitle:@"Reload" handler:^{ [weakSelf reload]; }]]; + // Add extra items + [items addObjectsFromArray:_extraMenuItems]; return items; } @@ -236,11 +213,11 @@ - (void)addItem:(__unused HippyDevMenuItem *)item { NSString *title = [NSString stringWithFormat:@"Hippy: Development (%@)", [_bridge moduleName]]; // On larger devices we don't have an anchor point for the action sheet - UIAlertControllerStyle style = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone ? UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert; - UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:title - message:@"" - preferredStyle:style]; - _actionSheet = actionSheet; + UIAlertControllerStyle style = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone ? + UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert; + _actionSheet = [UIAlertController alertControllerWithTitle:title + message:nil + preferredStyle:style]; NSArray *items = [self menuItems]; for (HippyDevMenuItem *item in items) { @@ -264,7 +241,7 @@ - (void)addItem:(__unused HippyDevMenuItem *)item { handler:^(__unused UIAlertAction *action) { }]]; - [HippyPresentedViewController() presentViewController:_actionSheet animated:YES completion:^(void){}]; + [HippyPresentedViewController() presentViewController:_actionSheet animated:YES completion:nil]; } @end diff --git a/modules/ios/base/HippyUtils.h b/modules/ios/base/HippyUtils.h index 96f36e02d79..1e1a17dc098 100644 --- a/modules/ios/base/HippyUtils.h +++ b/modules/ios/base/HippyUtils.h @@ -42,6 +42,7 @@ HIPPY_EXTERN void HippyExecuteOnMainThread(dispatch_block_t block, BOOL sync); // Method swizzling HIPPY_EXTERN void HippySwapClassMethods(Class cls, SEL original, SEL replacement); HIPPY_EXTERN void HippySwapInstanceMethods(Class cls, SEL original, SEL replacement); +HIPPY_EXTERN void HippySwapInstanceMethodWithBlock(Class cls, SEL original, id replacementBlock, SEL replacementSelector); // Module subclass support HIPPY_EXTERN BOOL HippyClassOverridesClassMethod(Class cls, SEL selector); diff --git a/modules/ios/base/HippyUtils.m b/modules/ios/base/HippyUtils.m index 448fea984c1..e9eb53b5aa8 100644 --- a/modules/ios/base/HippyUtils.m +++ b/modules/ios/base/HippyUtils.m @@ -87,6 +87,18 @@ void HippySwapInstanceMethods(Class cls, SEL original, SEL replacement) { } } +void HippySwapInstanceMethodWithBlock(Class cls, SEL original, id replacementBlock, SEL replacementSelector) { + Method originalMethod = class_getInstanceMethod(cls, original); + if (!originalMethod) { + return; + } + + IMP implementation = imp_implementationWithBlock(replacementBlock); + class_addMethod(cls, replacementSelector, implementation, method_getTypeEncoding(originalMethod)); + Method newMethod = class_getInstanceMethod(cls, replacementSelector); + method_exchangeImplementations(originalMethod, newMethod); +} + BOOL HippyClassOverridesClassMethod(Class cls, SEL selector) { return HippyClassOverridesInstanceMethod(object_getClass(cls), selector); } From 772729cbafcc11edb06a349b484a6c092c8aab65 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 24 Apr 2024 18:41:28 +0800 Subject: [PATCH 15/28] chore(ios): eliminate some warnings --- dom/src/dom/taitank_layout_node.cc | 2 +- modules/footstone/include/footstone/logging.h | 5 +++-- renderer/native/ios/renderer/HippyComponent.h | 8 ++++---- .../renderer/component/listview/HippyNextBaseListView.mm | 2 +- .../native/ios/renderer/component/view/HippyShadowView.mm | 4 ++-- .../native/ios/renderer/component/view/UIView+Hippy.mm | 7 +++---- .../ios/renderer/component/viewPager/HippyViewPager.mm | 2 +- .../component/waterfalllist/HippyShadowListView.mm | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/dom/src/dom/taitank_layout_node.cc b/dom/src/dom/taitank_layout_node.cc index b4b5bba339f..a21024d6c73 100644 --- a/dom/src/dom/taitank_layout_node.cc +++ b/dom/src/dom/taitank_layout_node.cc @@ -157,7 +157,7 @@ TAITANK_GET_STYLE_DECL(Direction, TaitankDirection, TaitankDirection::DIRECTION_ } static void CheckValueType(footstone::value::HippyValue::Type type) { - if (type == footstone::value::HippyValue::Type::kNumber || type == footstone::value::HippyValue::Type::kObject) + if (type == footstone::value::HippyValue::Type::kString || type == footstone::value::HippyValue::Type::kObject) FOOTSTONE_DLOG(WARNING) << "Taitank Layout Node Value Type Error"; } diff --git a/modules/footstone/include/footstone/logging.h b/modules/footstone/include/footstone/logging.h index dcf4a9cdfb0..9c0bc6eb469 100644 --- a/modules/footstone/include/footstone/logging.h +++ b/modules/footstone/include/footstone/logging.h @@ -108,6 +108,7 @@ class LogMessage { } delegate_ = delegate; } + inline static void LogWithFormat(const char * file, int line, const char *format, ...){ char *log_msg = NULL; va_list args; @@ -124,9 +125,9 @@ class LogMessage { <<"[thread:"<)subview atIndex:(NSInteger)atIndex; +/// - atIndex: NSUInteger +- (void)insertHippySubview:(id)subview atIndex:(NSUInteger)atIndex; /// Remove /// - Parameter subview: id @@ -62,8 +62,8 @@ typedef void (^HippyDirectEventBlock)(NSDictionary *body); /// Move /// - Parameters: /// - subview: id -/// - atIndex: NSInteger -- (void)moveHippySubview:(id)subview toIndex:(NSInteger)atIndex; +/// - atIndex: NSUInteger +- (void)moveHippySubview:(id)subview toIndex:(NSUInteger)atIndex; /// Remove from superview - (void)removeFromHippySuperview; diff --git a/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm b/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm index a3b6070b121..2a5a19c951c 100644 --- a/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm +++ b/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm @@ -163,7 +163,7 @@ - (void)reloadData { } } -- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(UIView *)subview atIndex:(NSUInteger)atIndex { if ([subview isKindOfClass:[HippyHeaderRefresh class]]) { if (_headerRefreshView) { [_headerRefreshView unsetFromScrollView]; diff --git a/renderer/native/ios/renderer/component/view/HippyShadowView.mm b/renderer/native/ios/renderer/component/view/HippyShadowView.mm index dc565ff3b07..650f8065ca5 100644 --- a/renderer/native/ios/renderer/component/view/HippyShadowView.mm +++ b/renderer/native/ios/renderer/component/view/HippyShadowView.mm @@ -177,7 +177,7 @@ - (UIView *)createView:(HippyViewCreationBlock)creationBlock insertChildren:(Hip return container; } -- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSUInteger)atIndex { if (atIndex <= [_objectSubviews count]) { [_objectSubviews insertObject:subview atIndex:atIndex]; } @@ -190,7 +190,7 @@ - (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex [self dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; } -- (void)moveHippySubview:(id)subview toIndex:(NSInteger)atIndex { +- (void)moveHippySubview:(id)subview toIndex:(NSUInteger)atIndex { if ([_objectSubviews containsObject:subview]) { [_objectSubviews removeObject:subview]; } diff --git a/renderer/native/ios/renderer/component/view/UIView+Hippy.mm b/renderer/native/ios/renderer/component/view/UIView+Hippy.mm index 439ccc2bc88..161e4b715b7 100644 --- a/renderer/native/ios/renderer/component/view/UIView+Hippy.mm +++ b/renderer/native/ios/renderer/component/view/UIView+Hippy.mm @@ -158,7 +158,7 @@ - (void)setParent:(id)parent { } } -- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(UIView *)subview atIndex:(NSUInteger)atIndex { // We access the associated object directly here in case someone overrides // the `subcomponents` getter method and returns an immutable array. if (nil == subview) { @@ -172,14 +172,13 @@ - (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { if (atIndex <= [subviews count]) { [subviews insertObject:subview atIndex:atIndex]; - } - else { + } else { [subviews addObject:subview]; } subview.parent = self; } -- (void)moveHippySubview:(UIView *)subview toIndex:(NSInteger)atIndex { +- (void)moveHippySubview:(UIView *)subview toIndex:(NSUInteger)atIndex { if (nil == subview) { return; } diff --git a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm index cbb18c9e3b8..4ad6bc61a20 100644 --- a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm +++ b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm @@ -85,7 +85,7 @@ - (void)didMoveToSuperview { #pragma mark native render native methods -- (void)insertHippySubview:(UIView *)view atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(UIView *)view atIndex:(NSUInteger)atIndex { if (atIndex > self.viewPagerItems.count) { HippyLogWarn(@"Error In HippyViewPager: addSubview —— out of bound of array"); return; diff --git a/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm b/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm index 484ce2b462e..cf19e5c4556 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm @@ -150,7 +150,7 @@ - (WaterfallItemChangeContext *)itemChangeContext { return _itemChangeContext; } -- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSUInteger)atIndex { [super insertHippySubview:subview atIndex:atIndex]; if ([subview isKindOfClass:[HippyShadowWaterfallItem class]]) { HippyShadowWaterfallItem *objectItem = (HippyShadowWaterfallItem *)subview; @@ -168,7 +168,7 @@ - (void)removeHippySubview:(HippyShadowView *)subview { [_itemChangeContext appendDeletedItem:subview]; } -- (void)moveHippySubview:(id)subview toIndex:(NSInteger)atIndex { +- (void)moveHippySubview:(id)subview toIndex:(NSUInteger)atIndex { [super moveHippySubview:subview toIndex:atIndex]; [_itemChangeContext appendMovedItem:subview]; } From 6a7abc8731196b6389002c810c23872534c40c3a Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 24 Apr 2024 19:43:56 +0800 Subject: [PATCH 16/28] fix(ios): eliminate Devtools connection warning --- framework/ios/base/executors/HippyJSExecutor.h | 2 -- framework/ios/base/executors/HippyJSExecutor.mm | 11 +++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/framework/ios/base/executors/HippyJSExecutor.h b/framework/ios/base/executors/HippyJSExecutor.h index 98c730c74f4..83ea6c17054 100644 --- a/framework/ios/base/executors/HippyJSExecutor.h +++ b/framework/ios/base/executors/HippyJSExecutor.h @@ -78,8 +78,6 @@ HIPPY_EXTERN NSString *const HippyJSCThreadName; */ @property (atomic, assign) std::shared_ptr pScope; -@property (nonatomic, copy) NSString *contextName; - @property(nonatomic, copy) HippyContextCreatedBlock contextCreatedBlock; - (instancetype)initWithEngineKey:(NSString *)engineKey bridge:(HippyBridge *)bridge; diff --git a/framework/ios/base/executors/HippyJSExecutor.mm b/framework/ios/base/executors/HippyJSExecutor.mm index f3b9e55a2fc..21f305e39c6 100644 --- a/framework/ios/base/executors/HippyJSExecutor.mm +++ b/framework/ios/base/executors/HippyJSExecutor.mm @@ -228,9 +228,11 @@ - (void)setup { HippyBridge *bridge = self.bridge; if (bridge && bridge.debugMode) { NSString *wsURL = [self completeWSURLWithBridge:bridge]; - auto workerManager = std::make_shared(1); - auto devtools_data_source = std::make_shared([wsURL UTF8String], workerManager); - self.pScope->SetDevtoolsDataSource(devtools_data_source); + if (wsURL.length > 0) { + auto workerManager = std::make_shared(1); + auto devtools_data_source = std::make_shared([wsURL UTF8String], workerManager); + self.pScope->SetDevtoolsDataSource(devtools_data_source); + } } #endif } @@ -331,9 +333,6 @@ - (SharedCtxValuePtr)JSTurboObjectWithName:(NSString *)name { return obj; } -- (void)setUp { -} - - (void)invalidate { if (!self.isValid) { return; From bd66bac67b45e7246821b207e6c2d1cff5bb2a6d Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 24 Apr 2024 20:16:10 +0800 Subject: [PATCH 17/28] fix(ios): image capInsets and getSize's bug Stay compatible with hippy2 --- modules/ios/image/HippyDefaultImageProvider.m | 15 ++++++++++++--- modules/ios/image/HippyImageProviderProtocol.h | 5 +++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/ios/image/HippyDefaultImageProvider.m b/modules/ios/image/HippyDefaultImageProvider.m index b8d1d5ebf74..1e3db65e4bc 100644 --- a/modules/ios/image/HippyDefaultImageProvider.m +++ b/modules/ios/image/HippyDefaultImageProvider.m @@ -35,7 +35,8 @@ @interface HippyDefaultImageProvider () { @implementation HippyDefaultImageProvider -@synthesize imageDataPath; +@synthesize scale = _scale; +@synthesize imageDataPath = _imageDataPath; + (BOOL)canHandleData:(NSData *)data { return YES; @@ -46,6 +47,14 @@ + (BOOL)isAnimatedImage:(NSData *)data { return ret; } +- (instancetype)init { + self = [super init]; + if (self) { + _scale = 1.0; + } + return self; +} + - (void)setImageData:(NSData *)imageData { if ([[self class] isAnimatedImage:imageData]) { _imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); @@ -121,13 +130,13 @@ - (void)prepareForDisplay:(void (^)(UIImage *_Nullable))completionHandler{ } - (UIImage *)image { - CGFloat scale = [UIScreen mainScreen].scale; if (!_image) { UIImage *tmp; if (_data) { CGFloat view_width = _imageViewSize.width; CGFloat view_height = _imageViewSize.height; if (_downSample && view_width > 0 && view_height > 0) { + CGFloat scale = self.scale; NSDictionary *options = @{ (NSString *)kCGImageSourceShouldCache: @(NO) }; CGImageSourceRef ref = CGImageSourceCreateWithData((__bridge CFDataRef)_data, (__bridge CFDictionaryRef)options); if (ref) { @@ -161,7 +170,7 @@ - (UIImage *)image { tmp = [self imageAtFrame:0]; } if(!tmp){ - tmp = [UIImage imageWithData:_data scale:scale]; + tmp = [UIImage imageWithData:_data scale:self.scale]; } @synchronized (self) { if(_image == nil){ diff --git a/modules/ios/image/HippyImageProviderProtocol.h b/modules/ios/image/HippyImageProviderProtocol.h index 406a11271b2..54a64015513 100644 --- a/modules/ios/image/HippyImageProviderProtocol.h +++ b/modules/ios/image/HippyImageProviderProtocol.h @@ -36,6 +36,11 @@ */ + (BOOL)isAnimatedImage:(NSData *)data; +/** + * Image scale + */ +@property (nonatomic, assign) CGFloat scale; + @property(nonatomic, copy)NSString *imageDataPath; /** From fdce688d468a2f69eccbf787c645be41e7a701e9 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 25 Apr 2024 18:04:10 +0800 Subject: [PATCH 18/28] fix(ios): add null protection before invalid move operation --- renderer/native/ios/renderer/HippyUIManager.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index c5bdcf7d049..e23aedcf84c 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -962,6 +962,10 @@ - (void)renderMoveViews:(const std::vector &&)ids HippyShadowView *toShadowView = [_shadowViewRegistry componentForTag:@(toContainer) onRootTag:@(rootTag)]; for (int32_t hippyTag : ids) { HippyShadowView *view = [_shadowViewRegistry componentForTag:@(hippyTag) onRootTag:@(rootTag)]; + if (!view) { + HippyLogWarn(@"Invalid Move, No ShadowView! (%d of %d)", hippyTag, rootTag); + continue; + } HippyAssert(fromShadowView == [view parent], @"ShadowView(%d)'s parent should be %d", hippyTag, fromContainer); [view removeFromHippySuperview]; [toShadowView insertHippySubview:view atIndex:index]; From bb11f1d09be49e33d3480fda37492d94437717a6 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 25 Apr 2024 17:54:14 +0800 Subject: [PATCH 19/28] fix(ios): lineHeight attribute was incorrectly inherited --- .../component/text/HippyShadowText.mm | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/renderer/native/ios/renderer/component/text/HippyShadowText.mm b/renderer/native/ios/renderer/component/text/HippyShadowText.mm index 46b3e36c56e..eb79f1399b7 100644 --- a/renderer/native/ios/renderer/component/text/HippyShadowText.mm +++ b/renderer/native/ios/renderer/component/text/HippyShadowText.mm @@ -450,8 +450,7 @@ - (NSAttributedString *)_attributedStringWithStyleInfo:(HippyAttributedStringSty if (!_textAlignSet) { if ([self isLayoutSubviewsRTL]) { self.textAlign = NSTextAlignmentRight; - } - else { + } else { self.textAlign = NSTextAlignmentLeft; } } @@ -488,18 +487,18 @@ - (NSAttributedString *)_attributedStringWithStyleInfo:(HippyAttributedStringSty } UIFont *font = [HippyFont updateFont:f - withFamily:styleInfo.fontFamily - size:styleInfo.fontSize - weight:styleInfo.fontWeight - style:styleInfo.fontStyle - variant:_fontVariant - scaleMultiplier:_allowFontScaling ? _fontSizeMultiplier : 1.0]; + withFamily:styleInfo.fontFamily + size:styleInfo.fontSize + weight:styleInfo.fontWeight + style:styleInfo.fontStyle + variant:_fontVariant + scaleMultiplier:_allowFontScaling ? _fontSizeMultiplier : 1.0]; CGFloat heightOfTallestSubview = 0.0; NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:self.text ?: @""]; for (HippyShadowView *child in [self subcomponents]) { if ([child isKindOfClass:[HippyShadowText class]]) { - HippyShadowText *shadowText = (HippyShadowText *)child; + HippyShadowText *childShadowText = (HippyShadowText *)child; HippyAttributedStringStyleInfo *childInfo = [HippyAttributedStringStyleInfo new]; childInfo.fontFamily = styleInfo.fontFamily; childInfo.fontSize = styleInfo.fontSize; @@ -507,11 +506,11 @@ - (NSAttributedString *)_attributedStringWithStyleInfo:(HippyAttributedStringSty childInfo.fontStyle = styleInfo.fontStyle; childInfo.letterSpacing = styleInfo.letterSpacing; childInfo.useBackgroundColor = YES; - childInfo.foregroundColor = [shadowText color] ?: styleInfo.foregroundColor; - childInfo.backgroundColor = shadowText.backgroundColor ?: styleInfo.backgroundColor; - childInfo.opacity = styleInfo.opacity * shadowText.opacity; + childInfo.foregroundColor = [childShadowText color] ?: styleInfo.foregroundColor; + childInfo.backgroundColor = childShadowText.backgroundColor ?: styleInfo.backgroundColor; + childInfo.opacity = styleInfo.opacity * childShadowText.opacity; childInfo.isNestedText = styleInfo.isNestedText; - NSAttributedString *subStr = [shadowText _attributedStringWithStyleInfo:childInfo]; + NSAttributedString *subStr = [childShadowText _attributedStringWithStyleInfo:childInfo]; [attributedString appendAttributedString:subStr]; [child setTextComputed]; } else { @@ -630,10 +629,11 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib heightOfTallestSubview:(CGFloat)heightOfTallestSubview isNestedText:(BOOL)isNestedText { NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; + CGFloat adjustedLineHeight = self.lineHeight; BOOL hasSetLineHeight = NO; - if (fabs(self.lineHeight - 0) < DBL_EPSILON) { + if (DirtyTextEqual(adjustedLineHeight, 0.0)) { // If no fixed lineHeight is set, fontLineHeight is used. - self.lineHeight = fontLineHeight; + adjustedLineHeight = fontLineHeight; } else if (!self.adjustsFontSizeToFit) { // Only when adjustsFontSizeToFit is not set, the fixed lineHeight can be used. hasSetLineHeight = YES; @@ -645,10 +645,10 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib hasParagraphStyle = YES; } - __block float newLineHeight = _lineHeight ?: 0.0; + __block CGFloat newLineHeight = adjustedLineHeight ?: 0.0; CGFloat fontSizeMultiplier = _allowFontScaling ? _fontSizeMultiplier : 1.0; - // check for lineHeight on each of our children, update the max as we go (in self.lineHeight) + // check for lineHeight on each of our children, update the max as we go [attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions @@ -662,9 +662,8 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib hasParagraphStyle = YES; } }]; - - if (self.lineHeight != newLineHeight) { - self.lineHeight = newLineHeight; + if (!DirtyTextEqual(adjustedLineHeight, newLineHeight)) { + adjustedLineHeight = newLineHeight; } __block CGFloat maximumFontLineHeight = 0.0; @@ -685,7 +684,7 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib if (hasParagraphStyle) { NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; paragraphStyle.alignment = _textAlign; - CGFloat lineHeight = round(_lineHeight * fontSizeMultiplier); + CGFloat lineHeight = round(adjustedLineHeight * fontSizeMultiplier); CGFloat maxHeight = lineHeight; if (heightOfTallestSubview > lineHeight) { maxHeight = ceilf(heightOfTallestSubview); From a0ea9a041cd46b9343ee6790c7e5e3f6c3f1c49c Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 8 May 2024 20:42:19 +0800 Subject: [PATCH 20/28] fix(ios): add protection against null value exceptions --- renderer/native/ios/renderer/HippyUIManager.mm | 5 +---- .../ios/renderer/component/view/HippyShadowView.mm | 9 +++++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index e23aedcf84c..1bf2deedb4b 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -221,9 +221,6 @@ - (instancetype)init { return self; } -- (void)dealloc { -} - - (void)initContext { _shadowViewRegistry = [[HippyComponentMap alloc] initWithComponentsReferencedType:HippyComponentReferenceTypeStrong]; _viewRegistry = [[HippyComponentMap alloc] initWithComponentsReferencedType:HippyComponentReferenceTypeWeak]; @@ -957,7 +954,7 @@ - (void)renderMoveViews:(const std::vector &&)ids return; } int32_t rootTag = strongRootNode->GetId(); - + std::lock_guard lock([self renderQueueLock]); HippyShadowView *fromShadowView = [_shadowViewRegistry componentForTag:@(fromContainer) onRootTag:@(rootTag)]; HippyShadowView *toShadowView = [_shadowViewRegistry componentForTag:@(toContainer) onRootTag:@(rootTag)]; for (int32_t hippyTag : ids) { diff --git a/renderer/native/ios/renderer/component/view/HippyShadowView.mm b/renderer/native/ios/renderer/component/view/HippyShadowView.mm index 650f8065ca5..84efcb1e904 100644 --- a/renderer/native/ios/renderer/component/view/HippyShadowView.mm +++ b/renderer/native/ios/renderer/component/view/HippyShadowView.mm @@ -27,6 +27,7 @@ #import "UIView+DirectionalLayout.h" #import "UIView+Hippy.h" #import "HippyShadowView+Internal.h" +#import "HippyAssert.h" static NSString *const HippyBackgroundColorPropKey = @"backgroundColor"; @@ -178,10 +179,14 @@ - (UIView *)createView:(HippyViewCreationBlock)creationBlock insertChildren:(Hip } - (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSUInteger)atIndex { + if (!subview) { + HippyAssert(subview != nil, @"subview should not be nil!"); + HippyFatal(HippyErrorWithMessage(@"Illegal nil shadow subview in insertHippySubview!")); + return; + } if (atIndex <= [_objectSubviews count]) { [_objectSubviews insertObject:subview atIndex:atIndex]; - } - else { + } else { [_objectSubviews addObject:subview]; } subview->_superview = self; From f38ae3c4e5a358627bb63ed0253ed2fa5d818932 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 15 May 2024 17:57:56 +0800 Subject: [PATCH 21/28] chore(build): update husky commit hook command --- .husky/post-checkout | 2 +- .husky/post-commit | 2 +- .husky/post-merge | 2 +- .husky/pre-push | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.husky/post-checkout b/.husky/post-checkout index c37815e2b56..ca7fcb40088 100755 --- a/.husky/post-checkout +++ b/.husky/post-checkout @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-checkout'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-checkout' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-checkout "$@" diff --git a/.husky/post-commit b/.husky/post-commit index e5230c305f9..52b339cb3f4 100755 --- a/.husky/post-commit +++ b/.husky/post-commit @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-commit'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-commit' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-commit "$@" diff --git a/.husky/post-merge b/.husky/post-merge index c99b752a527..a912e667aa3 100755 --- a/.husky/post-merge +++ b/.husky/post-merge @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-merge'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-merge' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-merge "$@" diff --git a/.husky/pre-push b/.husky/pre-push index 216e91527e6..0f0089bc25d 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs pre-push "$@" From 4249f3ab616bdc38a92e4109ce761ba1ab16e471 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 May 2024 15:25:27 +0800 Subject: [PATCH 22/28] fix(ios): null exception when converting JSI object to dictionary --- framework/ios/module/turbo/HippyOCTurboModule.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/ios/module/turbo/HippyOCTurboModule.mm b/framework/ios/module/turbo/HippyOCTurboModule.mm index 840f7282316..0741570dd92 100644 --- a/framework/ios/module/turbo/HippyOCTurboModule.mm +++ b/framework/ios/module/turbo/HippyOCTurboModule.mm @@ -315,7 +315,7 @@ static id convertJSIObjectToNSObject(const std::shared_ptr &co } std::u16string u16Key = StringViewUtils::ConvertEncoding(string_view, string_view::Encoding::Utf16).utf16_value(); NSString *stringKey = [NSString stringWithCharacters:(const unichar*)u16Key.c_str() length:(u16Key.length())]; - id objValue = convertCtxValueToObjcObject(context, value, module); + id objValue = convertCtxValueToObjcObject(context, value, module) ?: [NSNull null]; [result setObject:objValue forKey:stringKey]; } return [result copy]; From 4769911d34c54ca1b7d45b9fd46fdab6a1e38353 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 May 2024 21:41:45 +0800 Subject: [PATCH 23/28] feat(ios): update ViewPager's pageIndex automatically after data changes (#3857) * feat(ios): pageIndex of ViewPager auto update after data changes Also fixed the issue of missing onPageSelected callbacks upon first entry; The logic for automatic updates keep same with Android, as follows 1. If the previous item only changes its location, update the current location and keep the current item displayed. 2. If the previous item does not exist, do not adjust the position, but keep the current position in the valid range (that is, 0 ~ count-1). * fix(ios): auto-update of page index may lag by one frame in view pager this is a minor fix for feat 'pageIndex of ViewPager auto update after data changes' --- .../component/viewPager/HippyViewPager.mm | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm index 4ad6bc61a20..a19dc6f4d6f 100644 --- a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm +++ b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm @@ -46,6 +46,10 @@ @interface HippyViewPager () @property (nonatomic, assign) CGFloat previousStopOffset; @property (nonatomic, assign) NSUInteger lastPageSelectedCallbackIndex; +/// A weak property used to record the currently displayed item, +/// which is used for updating the page index when the data changes. +@property (nonatomic, weak) UIView *lastSelectedPageItem; + @end @implementation HippyViewPager @@ -62,6 +66,7 @@ - (instancetype)initWithFrame:(CGRect)frame { self.previousFrame = CGRectZero; self.scrollViewListener = [NSHashTable weakObjectsHashTable]; self.lastPageIndex = NSUIntegerMax; + self.lastPageSelectedCallbackIndex = NSUIntegerMax; self.targetContentOffsetX = CGFLOAT_MAX; if (@available(iOS 11.0, *)) { self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; @@ -149,6 +154,25 @@ - (void)hippySetFrame:(CGRect)frame { - (void)didUpdateHippySubviews { [super didUpdateHippySubviews]; self.needsLayoutItems = YES; + + // Update the latest page index based on the currently displayed item (aka lastSelectedPageItem). + // Keep the same logic as android: + // 1. If the previous item only changes its location, + // update the current location and keep the current item displayed. + // 2. If the previous item does not exist, do not adjust the position, + // but keep the current position in the valid range (that is, 0 ~ count-1). + UIView *previousSelectedItem = self.lastSelectedPageItem; + NSUInteger updatedPageIndex; + if (previousSelectedItem) { + updatedPageIndex = [self.viewPagerItems indexOfObject:previousSelectedItem]; + } else { + updatedPageIndex = MAX(0, MIN(self.lastPageIndex, self.viewPagerItems.count - 1)); + } + if (self.lastPageIndex != updatedPageIndex) { + self.lastPageIndex = updatedPageIndex; + self.needsResetPageIndex = YES; + } + [self setNeedsLayout]; } @@ -161,6 +185,7 @@ - (void)setPage:(NSInteger)pageNumber animated:(BOOL)animated { _lastPageIndex = pageNumber; UIView *theItem = self.viewPagerItems[pageNumber]; + self.lastSelectedPageItem = theItem; self.targetContentOffsetX = CGRectGetMinX(theItem.frame); [self setContentOffset:theItem.frame.origin animated:animated]; [self invokePageSelected:pageNumber]; @@ -363,6 +388,7 @@ - (NSUInteger)targetPageIndexFromTargetContentOffsetX:(CGFloat)targetContentOffs } if (_lastPageIndex != thePage) { _lastPageIndex = thePage; + _lastSelectedPageItem = self.viewPagerItems[thePage]; return thePage; } else { return _lastPageIndex; @@ -389,7 +415,6 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated { - (void)hippyBridgeDidFinishTransaction { BOOL isFrameEqual = CGRectEqualToRect(self.frame, self.previousFrame); BOOL isContentSizeEqual = CGSizeEqualToSize(self.contentSize, self.previousSize); - if (!isContentSizeEqual || !isFrameEqual) { self.previousFrame = self.frame; self.previousSize = self.contentSize; @@ -425,10 +450,12 @@ - (void)layoutSubviews { return; } - self.contentSize = CGSizeMake( - lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width, - lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height - ); + CGSize updatedSize = CGSizeMake(lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width, + lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height); + if (!CGSizeEqualToSize(self.contentSize, updatedSize)) { + self.contentSize = updatedSize; + } + if (!_didFirstTimeLayout) { [self setPage:self.initialPage animated:NO]; _didFirstTimeLayout = YES; From 0c4bfef6fedf7e579798e56cb9e4202e72c7f28e Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 May 2024 16:36:41 +0800 Subject: [PATCH 24/28] test(ios): add xctestplan file --- .../examples/ios-demo/HippyDemo.xctestplan | 22 +++++++++++++ .../ios-demo/hippy-Unit-UnitTests.xctestplan | 32 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 framework/examples/ios-demo/HippyDemo.xctestplan create mode 100644 framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan diff --git a/framework/examples/ios-demo/HippyDemo.xctestplan b/framework/examples/ios-demo/HippyDemo.xctestplan new file mode 100644 index 00000000000..9ab53771413 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xctestplan @@ -0,0 +1,22 @@ +{ + "configurations" : [ + { + "id" : "C642C4E4-3340-4058-BCDB-F8D3BE31926A", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "targetForVariableExpansion" : { + "containerPath" : "container:HippyDemo.xcodeproj", + "identifier" : "F7DF50CA19E5E79F64D3D4E6", + "name" : "HippyDemo" + } + }, + "testTargets" : [ + + ], + "version" : 1 +} diff --git a/framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan b/framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan new file mode 100644 index 00000000000..a32bdc00674 --- /dev/null +++ b/framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan @@ -0,0 +1,32 @@ +{ + "configurations" : [ + { + "id" : "EA4E4CF7-9024-4B4A-B911-A0056EF7BAD7", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : { + "targets" : [ + { + "containerPath" : "container:Pods.xcodeproj", + "identifier" : "927329EF8F33B34F7F9A0655F6ABA7C3", + "name" : "hippy" + } + ] + } + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:Pods.xcodeproj", + "identifier" : "C1E172B01D45B4ADDB866126B685CA58", + "name" : "hippy-Unit-UnitTests" + } + } + ], + "version" : 1 +} From b95869e0848c2e1fc73200d3cc067335c8d5e67b Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 16 May 2024 17:07:42 +0800 Subject: [PATCH 25/28] build(ios): remove xcodegen usage due to test support issues --- .github/workflows/ios_build_tests.yml | 5 - .../workflows/project_artifact_compare.yml | 6 - .gitignore | 1 - README.md | 2 +- docs/development/demo.md | 8 +- .../HippyDemo.xcodeproj/project.pbxproj | 576 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/HippyDemo.xcscheme | 78 +++ framework/examples/ios-demo/README.md | 8 +- framework/examples/ios-demo/project.yml | 25 - 10 files changed, 671 insertions(+), 45 deletions(-) create mode 100644 framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj create mode 100644 framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme delete mode 100644 framework/examples/ios-demo/project.yml diff --git a/.github/workflows/ios_build_tests.yml b/.github/workflows/ios_build_tests.yml index 6c28967abf7..3c3b8420ae1 100644 --- a/.github/workflows/ios_build_tests.yml +++ b/.github/workflows/ios_build_tests.yml @@ -38,11 +38,6 @@ jobs: uses: actions/checkout@v3 with: lfs: true - - name: Xcodegen - uses: xavierLowmiller/xcodegen-action@1.1.2 - with: - spec: framework/examples/ios-demo/project.yml - version: '2.32.0' - name: Demo working-directory: framework/examples/ios-demo run: | diff --git a/.github/workflows/project_artifact_compare.yml b/.github/workflows/project_artifact_compare.yml index 2c0980704e5..bdb37c8c999 100644 --- a/.github/workflows/project_artifact_compare.yml +++ b/.github/workflows/project_artifact_compare.yml @@ -89,16 +89,10 @@ jobs: with: ref: ${{ matrix.ref }} lfs: true - - name: Xcodegen - uses: xavierLowmiller/xcodegen-action@1.1.2 - with: - spec: framework/examples/ios-demo/project.yml - version: '2.32.0' - name: Build if: ${{ matrix.ref }} run: | pushd framework/examples/ios-demo - xcodegen pod install xcodebuild build \ -destination 'generic/platform=iOS' \ diff --git a/.gitignore b/.gitignore index 01bdcb1af9d..33cc2630ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ framework/examples/android-demo/src/main/assets/ framework/examples/android-demo/libs/* framework/examples/android-demo/maven-auth.properties framework/examples/android-demo/.cxx/ -framework/examples/ios-demo/HippyDemo.xcodeproj framework/examples/ios-demo/HippyDemo.xcworkspace framework/examples/ios-demo/Pods/* framework/examples/ios-demo/Podfile.lock diff --git a/README.md b/README.md index 9e7e17ac22b..bbd6ae55a27 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ For iOS, we recommend to use iOS simulator when first try. However, you can chan 3. Choose a demo to build with `npm run buildexample [hippy-react-demo|hippy-vue-demo|hippy-vue-next-demo]`. -4. Install Xcodegen with `brew install xcodegen`, install CocoaPods with `brew install cocoapods`, install cmake with `brew install cmake`, then execute `xcodegen` command at `framework/examples/ios-demo` directory, which will create `HippyDemo.xcodeproj` and `HippyDemo.xcworkspace` files and install Cocoapods dependencies. +4. Install CocoaPods with `brew install cocoapods`, install cmake with `brew install cmake`, then execute `pod install` command at `framework/examples/ios-demo` directory, which will create `HippyDemo.xcworkspace` files and install Cocoapods dependencies. 5. Start the Xcode and build the iOS app with opening `framework/examples/ios-demo/HippyDemo.xcworkspace`. diff --git a/docs/development/demo.md b/docs/development/demo.md index a8c0c4de6b8..bb970335588 100644 --- a/docs/development/demo.md +++ b/docs/development/demo.md @@ -22,7 +22,7 @@ Demo的Native工程代码位于framework/examples目录,前端工程代码位 首先,通过Homebrew包管理工具安装git, git-lfs, node(v16) and npm(v7) ```shell -brew install git git-lfs node@16 xcodegen cmake +brew install git git-lfs node@16 cmake ``` #### 编译iOS Demo环境准备 @@ -82,9 +82,9 @@ git clone https://github.com/Tencent/Hippy.git ```shell # 进入Hippy源码目录 cd ./framework/examples/ios-demo -# 执行xcodegen生成工程 -xcodegen -# 打开workspace,编译运行即可 +# 使用 Cocoapods 生成工程 +pod install +# 打开 workspace,编译运行即可 open HippyDemo.xcworkspace ``` diff --git a/framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..b5bf8471335 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj @@ -0,0 +1,576 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 16D159EB8DCA407DFE4008C8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D2EE3455BE6DD7D2ADA20093 /* main.m */; }; + 291382C2783D93A2C7BD4ACB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6094F1F80FD70F9F9853EE15 /* AppDelegate.m */; }; + 36842E3B36BB6E52267BED0F /* HomePageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC53FC9D9560D6AD989F8540 /* HomePageView.xib */; }; + 369577C613161936EE28F52E /* DebugCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BD54126F3E80B297E2109B40 /* DebugCell.m */; }; + 380469787621147052854E8F /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE0C5F943248577555BFB26 /* SettingsViewController.m */; }; + 4CBD515D378CB81C4C164353 /* DebugCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01F6A42F861E597779C10D09 /* DebugCell.xib */; }; + 5A5A9B087B27BD97E132C49E /* DemoBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4708C41E8571123282E2B806 /* DemoBaseViewController.m */; }; + 5A60628334CE16EA4F8B1329 /* DemoNavigationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 16348E2DEBFF10ADD6FCF7A9 /* DemoNavigationViewController.m */; }; + 72328C3389BA8DD5E54D7D5F /* TurboConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 46034630F6D4A25199673157 /* TurboConfig.m */; }; + 74C51526435B1AB788CBF991 /* TurboBaseModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC8AEAE8BF116954FB41BDF9 /* TurboBaseModule.mm */; }; + 8C009766BC90CAF781042636 /* PageCreationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E734E5A93F5B92EA4149B8D /* PageCreationViewController.m */; }; + 8D5E861571620B4DA80254C2 /* TTTGB-Medium.otf in CopyFiles */ = {isa = PBXBuildFile; fileRef = B2FA2A97C78628634C6AFC9A /* TTTGB-Medium.otf */; }; + 994E84462AD481034728A29D /* HippyPageCacheView.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BFD15025A6217FEE93CC45 /* HippyPageCacheView.m */; }; + 9C99A05896359E49AE9A8352 /* MyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 90F34E176169143334EDFC28 /* MyView.m */; }; + 9CF888F990C83ECD4F6C78AA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EF877A8CB4B2AD68A780A12 /* Assets.xcassets */; }; + A3F629CA4C94D65F42BB4B33 /* HippyDemoViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9EB0CD5FCC3F38E3C7DC5E00 /* HippyDemoViewController.mm */; }; + B4CB9EEA2C501CCD508731F6 /* Pods_HippyDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25D66C0C95F43BF303761823 /* Pods_HippyDemo.framework */; }; + B5E0049F9941EA38E80EC858 /* HippyDemoLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9D15F65FD369654F00146AD7 /* HippyDemoLoader.mm */; }; + B8AA2B793BE43710EFC9D4DC /* TestModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = D2241406CEF00D2FF5938BB2 /* TestModule.mm */; }; + BD5196B65EE0B712FDA2EB9B /* UIViewController+Title.m in Sources */ = {isa = PBXBuildFile; fileRef = C412B5D210F1E3359523628C /* UIViewController+Title.m */; }; + C1A78F8868963E6C790472AA /* PageCreationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 25B65684EC9C9AF27BB04D21 /* PageCreationCell.xib */; }; + C94B7BB8A618F36A1F58C270 /* HomePageViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 90B5552FD45612EC5EC4CB45 /* HomePageViewController.mm */; }; + D026B185E903AE8F958F8588 /* HippyPageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E7AA75BF1236A4328285E7 /* HippyPageCache.m */; }; + DCCEB3FED1802C0D9C21AB11 /* PageManagerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CC425F89818837D6DA1053A /* PageManagerViewController.m */; }; + DE19BAD300150C5D3D76D7F7 /* PageCreationCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 263EFA7461EF8C6CCDF67C66 /* PageCreationCell.m */; }; + DF04004944D28CE1569D68EC /* HippyPageCacheContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A066953E3614D26125F617EF /* HippyPageCacheContainerView.m */; }; + EBC6C5A3C36A88BF4DAD00BE /* res in Resources */ = {isa = PBXBuildFile; fileRef = 1E1EECB72C2ACDCB192E6628 /* res */; }; + F3F6D205C6FBA36CB262A28E /* SettingsInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C6BAC7BC0021E9E21B04E496 /* SettingsInfo.m */; }; + F67849BC0665859B70497D0E /* IconUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 17AB7FB6346FE861C9D953F5 /* IconUtils.m */; }; + FD3BE72F8A086DC9A9484195 /* MyViewManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 834EC46509EC221C787A9F23 /* MyViewManager.mm */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 171164B8638E25416B38386C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + 8D5E861571620B4DA80254C2 /* TTTGB-Medium.otf in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 01F6A42F861E597779C10D09 /* DebugCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DebugCell.xib; sourceTree = ""; }; + 041D598BDAC3735B5A06ECE7 /* TurboConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TurboConfig.h; sourceTree = ""; }; + 078054D939523BD635AFFB94 /* SettingsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; + 16348E2DEBFF10ADD6FCF7A9 /* DemoNavigationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoNavigationViewController.m; sourceTree = ""; }; + 16E50FF2E97E0CF6CD4B1EE1 /* MyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyView.h; sourceTree = ""; }; + 177A9E8373F2682A9A831991 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 17AB7FB6346FE861C9D953F5 /* IconUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IconUtils.m; sourceTree = ""; }; + 1E1EECB72C2ACDCB192E6628 /* res */ = {isa = PBXFileReference; lastKnownFileType = folder; path = res; sourceTree = SOURCE_ROOT; }; + 1E734E5A93F5B92EA4149B8D /* PageCreationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PageCreationViewController.m; sourceTree = ""; }; + 25B65684EC9C9AF27BB04D21 /* PageCreationCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageCreationCell.xib; sourceTree = ""; }; + 25D66C0C95F43BF303761823 /* Pods_HippyDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HippyDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 263EFA7461EF8C6CCDF67C66 /* PageCreationCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PageCreationCell.m; sourceTree = ""; }; + 27E7AA75BF1236A4328285E7 /* HippyPageCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyPageCache.m; sourceTree = ""; }; + 28C2DA6A96B596294158B364 /* HippyDemoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyDemoViewController.h; sourceTree = ""; }; + 331F0D867F0DF450A84E4587 /* Pods-HippyDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HippyDemo.debug.xcconfig"; path = "Target Support Files/Pods-HippyDemo/Pods-HippyDemo.debug.xcconfig"; sourceTree = ""; }; + 3A2AA203AF38A01360BEFB2A /* HippyDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HippyDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 427F60D1B98DA953328F4E71 /* DemoBaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoBaseViewController.h; sourceTree = ""; }; + 46034630F6D4A25199673157 /* TurboConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TurboConfig.m; sourceTree = ""; }; + 4708C41E8571123282E2B806 /* DemoBaseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoBaseViewController.m; sourceTree = ""; }; + 4BCCCB3005CE328BD310C417 /* HippyDemoLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyDemoLoader.h; sourceTree = ""; }; + 53A0957F7CC6967C8F70403A /* PageManagerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageManagerViewController.h; sourceTree = ""; }; + 5EF877A8CB4B2AD68A780A12 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6045C301C6CFDC6C2E87AA66 /* IconUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IconUtils.h; sourceTree = ""; }; + 6094F1F80FD70F9F9853EE15 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 6C482239432657812B25A59A /* SettingsInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsInfo.h; sourceTree = ""; }; + 73A859BDC9C3189E38FD082F /* MyViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyViewManager.h; sourceTree = ""; }; + 7CC425F89818837D6DA1053A /* PageManagerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PageManagerViewController.m; sourceTree = ""; }; + 7DB34DDF95B8E9C9AD87FFE4 /* Pods-HippyDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HippyDemo.release.xcconfig"; path = "Target Support Files/Pods-HippyDemo/Pods-HippyDemo.release.xcconfig"; sourceTree = ""; }; + 80D715362B1A0B0DCEAA813D /* DemoNavigationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoNavigationViewController.h; sourceTree = ""; }; + 82BFD15025A6217FEE93CC45 /* HippyPageCacheView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyPageCacheView.m; sourceTree = ""; }; + 834EC46509EC221C787A9F23 /* MyViewManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MyViewManager.mm; sourceTree = ""; }; + 8C70B069BA7C4A351FD26D60 /* HippyPageCacheView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyPageCacheView.h; sourceTree = ""; }; + 90B5552FD45612EC5EC4CB45 /* HomePageViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HomePageViewController.mm; sourceTree = ""; }; + 90F34E176169143334EDFC28 /* MyView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyView.m; sourceTree = ""; }; + 91861A8DD9D692FEDF657D34 /* HomePageViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomePageViewController.h; sourceTree = ""; }; + 9ABAD8FCDB669A1A6F327CDB /* DebugCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DebugCell.h; sourceTree = ""; }; + 9D15F65FD369654F00146AD7 /* HippyDemoLoader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HippyDemoLoader.mm; sourceTree = ""; }; + 9EB0CD5FCC3F38E3C7DC5E00 /* HippyDemoViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HippyDemoViewController.mm; sourceTree = ""; }; + A066953E3614D26125F617EF /* HippyPageCacheContainerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyPageCacheContainerView.m; sourceTree = ""; }; + B2FA2A97C78628634C6AFC9A /* TTTGB-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "TTTGB-Medium.otf"; sourceTree = ""; }; + B5A22F93EAA399DE400CED88 /* TestModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestModule.h; sourceTree = ""; }; + BD54126F3E80B297E2109B40 /* DebugCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DebugCell.m; sourceTree = ""; }; + BF84819C1E7CCD88BCBEA4D7 /* HippyPageCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyPageCache.h; sourceTree = ""; }; + C412B5D210F1E3359523628C /* UIViewController+Title.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Title.m"; sourceTree = ""; }; + C629FDE26F7B6DBC976FB63A /* TurboBaseModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TurboBaseModule.h; sourceTree = ""; }; + C6BAC7BC0021E9E21B04E496 /* SettingsInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsInfo.m; sourceTree = ""; }; + C7504BC18486120B29F3D9FA /* PageCreationCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageCreationCell.h; sourceTree = ""; }; + CC8AEAE8BF116954FB41BDF9 /* TurboBaseModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TurboBaseModule.mm; sourceTree = ""; }; + D2241406CEF00D2FF5938BB2 /* TestModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestModule.mm; sourceTree = ""; }; + D2EE3455BE6DD7D2ADA20093 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DC53FC9D9560D6AD989F8540 /* HomePageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomePageView.xib; sourceTree = ""; }; + DDD052D2760E830B1E0095EA /* UIViewController+Title.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Title.h"; sourceTree = ""; }; + E54E76502FD6AF687683BA8A /* HippyPageCacheContainerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyPageCacheContainerView.h; sourceTree = ""; }; + EEE0C5F943248577555BFB26 /* SettingsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; + EF558AABDC113B410B8ED69C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + F3C3E83580FEFC6EEE4CF186 /* DemoConfigs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoConfigs.h; sourceTree = ""; }; + FBB045A62B4E511674BF590C /* PageCreationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageCreationViewController.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 188D3F897881826DDEBE243C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B4CB9EEA2C501CCD508731F6 /* Pods_HippyDemo.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0632A691275B124491E38EA7 /* HippyDemo */ = { + isa = PBXGroup; + children = ( + EF558AABDC113B410B8ED69C /* AppDelegate.h */, + 6094F1F80FD70F9F9853EE15 /* AppDelegate.m */, + 427F60D1B98DA953328F4E71 /* DemoBaseViewController.h */, + 4708C41E8571123282E2B806 /* DemoBaseViewController.m */, + F3C3E83580FEFC6EEE4CF186 /* DemoConfigs.h */, + 80D715362B1A0B0DCEAA813D /* DemoNavigationViewController.h */, + 16348E2DEBFF10ADD6FCF7A9 /* DemoNavigationViewController.m */, + 6045C301C6CFDC6C2E87AA66 /* IconUtils.h */, + 17AB7FB6346FE861C9D953F5 /* IconUtils.m */, + 177A9E8373F2682A9A831991 /* Info.plist */, + D2EE3455BE6DD7D2ADA20093 /* main.m */, + B5A22F93EAA399DE400CED88 /* TestModule.h */, + D2241406CEF00D2FF5938BB2 /* TestModule.mm */, + DDD052D2760E830B1E0095EA /* UIViewController+Title.h */, + C412B5D210F1E3359523628C /* UIViewController+Title.m */, + 4CF6648308C60D57DD274D30 /* HomePage */, + DE44248AB3BFFAAE044EAB9E /* myview */, + 73EF815F203E15B86205DFC3 /* PageManager */, + 57D9249C5EA211061816F73B /* RenderPage */, + 09FA8898DDD429EC19584446 /* SettingsPage */, + 529ECDFF8342B9C9D950B1FA /* turbomodule */, + 260EEB3518979BB11CBFC5C8 /* vfsloader */, + ); + path = HippyDemo; + sourceTree = ""; + }; + 09FA8898DDD429EC19584446 /* SettingsPage */ = { + isa = PBXGroup; + children = ( + 6C482239432657812B25A59A /* SettingsInfo.h */, + C6BAC7BC0021E9E21B04E496 /* SettingsInfo.m */, + 078054D939523BD635AFFB94 /* SettingsViewController.h */, + EEE0C5F943248577555BFB26 /* SettingsViewController.m */, + ); + path = SettingsPage; + sourceTree = ""; + }; + 260EEB3518979BB11CBFC5C8 /* vfsloader */ = { + isa = PBXGroup; + children = ( + 4BCCCB3005CE328BD310C417 /* HippyDemoLoader.h */, + 9D15F65FD369654F00146AD7 /* HippyDemoLoader.mm */, + ); + path = vfsloader; + sourceTree = ""; + }; + 37AC1BAC994661BA846D4236 /* Products */ = { + isa = PBXGroup; + children = ( + 3A2AA203AF38A01360BEFB2A /* HippyDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 3F4F18D890009F63DB0F22D2 = { + isa = PBXGroup; + children = ( + 5EF877A8CB4B2AD68A780A12 /* Assets.xcassets */, + 1E1EECB72C2ACDCB192E6628 /* res */, + F3C07E1B41FBB8BA0834C6BC /* fonts */, + 0632A691275B124491E38EA7 /* HippyDemo */, + 37AC1BAC994661BA846D4236 /* Products */, + 7D0241712C232049EC5C8DC8 /* Pods */, + C24B18F307439A018EC32537 /* Frameworks */, + ); + sourceTree = ""; + }; + 4CF6648308C60D57DD274D30 /* HomePage */ = { + isa = PBXGroup; + children = ( + DC53FC9D9560D6AD989F8540 /* HomePageView.xib */, + 91861A8DD9D692FEDF657D34 /* HomePageViewController.h */, + 90B5552FD45612EC5EC4CB45 /* HomePageViewController.mm */, + ); + path = HomePage; + sourceTree = ""; + }; + 529ECDFF8342B9C9D950B1FA /* turbomodule */ = { + isa = PBXGroup; + children = ( + C629FDE26F7B6DBC976FB63A /* TurboBaseModule.h */, + CC8AEAE8BF116954FB41BDF9 /* TurboBaseModule.mm */, + 041D598BDAC3735B5A06ECE7 /* TurboConfig.h */, + 46034630F6D4A25199673157 /* TurboConfig.m */, + ); + path = turbomodule; + sourceTree = ""; + }; + 57D9249C5EA211061816F73B /* RenderPage */ = { + isa = PBXGroup; + children = ( + 28C2DA6A96B596294158B364 /* HippyDemoViewController.h */, + 9EB0CD5FCC3F38E3C7DC5E00 /* HippyDemoViewController.mm */, + ); + path = RenderPage; + sourceTree = ""; + }; + 73EF815F203E15B86205DFC3 /* PageManager */ = { + isa = PBXGroup; + children = ( + 9ABAD8FCDB669A1A6F327CDB /* DebugCell.h */, + BD54126F3E80B297E2109B40 /* DebugCell.m */, + 01F6A42F861E597779C10D09 /* DebugCell.xib */, + BF84819C1E7CCD88BCBEA4D7 /* HippyPageCache.h */, + 27E7AA75BF1236A4328285E7 /* HippyPageCache.m */, + E54E76502FD6AF687683BA8A /* HippyPageCacheContainerView.h */, + A066953E3614D26125F617EF /* HippyPageCacheContainerView.m */, + 8C70B069BA7C4A351FD26D60 /* HippyPageCacheView.h */, + 82BFD15025A6217FEE93CC45 /* HippyPageCacheView.m */, + C7504BC18486120B29F3D9FA /* PageCreationCell.h */, + 263EFA7461EF8C6CCDF67C66 /* PageCreationCell.m */, + 25B65684EC9C9AF27BB04D21 /* PageCreationCell.xib */, + FBB045A62B4E511674BF590C /* PageCreationViewController.h */, + 1E734E5A93F5B92EA4149B8D /* PageCreationViewController.m */, + 53A0957F7CC6967C8F70403A /* PageManagerViewController.h */, + 7CC425F89818837D6DA1053A /* PageManagerViewController.m */, + ); + path = PageManager; + sourceTree = ""; + }; + 7D0241712C232049EC5C8DC8 /* Pods */ = { + isa = PBXGroup; + children = ( + 331F0D867F0DF450A84E4587 /* Pods-HippyDemo.debug.xcconfig */, + 7DB34DDF95B8E9C9AD87FFE4 /* Pods-HippyDemo.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + C24B18F307439A018EC32537 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 25D66C0C95F43BF303761823 /* Pods_HippyDemo.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + DE44248AB3BFFAAE044EAB9E /* myview */ = { + isa = PBXGroup; + children = ( + 16E50FF2E97E0CF6CD4B1EE1 /* MyView.h */, + 90F34E176169143334EDFC28 /* MyView.m */, + 73A859BDC9C3189E38FD082F /* MyViewManager.h */, + 834EC46509EC221C787A9F23 /* MyViewManager.mm */, + ); + path = myview; + sourceTree = ""; + }; + F3C07E1B41FBB8BA0834C6BC /* fonts */ = { + isa = PBXGroup; + children = ( + B2FA2A97C78628634C6AFC9A /* TTTGB-Medium.otf */, + ); + path = fonts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F7DF50CA19E5E79F64D3D4E6 /* HippyDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 20BB0794C4C1CBC0A34760F4 /* Build configuration list for PBXNativeTarget "HippyDemo" */; + buildPhases = ( + 261C188D2073B9C801310C71 /* [CP] Check Pods Manifest.lock */, + 8C5357857E521ECD2E143CFD /* Sources */, + 81E836ED7DAB70A2CBF5FAFE /* Resources */, + 171164B8638E25416B38386C /* CopyFiles */, + 188D3F897881826DDEBE243C /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HippyDemo; + productName = HippyDemo; + productReference = 3A2AA203AF38A01360BEFB2A /* HippyDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 215A0FDFFBD7838975355873 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1530; + }; + buildConfigurationList = BD5ED1CB5E91D88413261758 /* Build configuration list for PBXProject "HippyDemo" */; + compatibilityVersion = "Xcode 11.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = 3F4F18D890009F63DB0F22D2; + productRefGroup = 37AC1BAC994661BA846D4236 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F7DF50CA19E5E79F64D3D4E6 /* HippyDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 81E836ED7DAB70A2CBF5FAFE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9CF888F990C83ECD4F6C78AA /* Assets.xcassets in Resources */, + 4CBD515D378CB81C4C164353 /* DebugCell.xib in Resources */, + 36842E3B36BB6E52267BED0F /* HomePageView.xib in Resources */, + C1A78F8868963E6C790472AA /* PageCreationCell.xib in Resources */, + EBC6C5A3C36A88BF4DAD00BE /* res in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 261C188D2073B9C801310C71 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-HippyDemo-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8C5357857E521ECD2E143CFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 291382C2783D93A2C7BD4ACB /* AppDelegate.m in Sources */, + 369577C613161936EE28F52E /* DebugCell.m in Sources */, + 5A5A9B087B27BD97E132C49E /* DemoBaseViewController.m in Sources */, + 5A60628334CE16EA4F8B1329 /* DemoNavigationViewController.m in Sources */, + B5E0049F9941EA38E80EC858 /* HippyDemoLoader.mm in Sources */, + A3F629CA4C94D65F42BB4B33 /* HippyDemoViewController.mm in Sources */, + D026B185E903AE8F958F8588 /* HippyPageCache.m in Sources */, + DF04004944D28CE1569D68EC /* HippyPageCacheContainerView.m in Sources */, + 994E84462AD481034728A29D /* HippyPageCacheView.m in Sources */, + C94B7BB8A618F36A1F58C270 /* HomePageViewController.mm in Sources */, + F67849BC0665859B70497D0E /* IconUtils.m in Sources */, + 9C99A05896359E49AE9A8352 /* MyView.m in Sources */, + FD3BE72F8A086DC9A9484195 /* MyViewManager.mm in Sources */, + DE19BAD300150C5D3D76D7F7 /* PageCreationCell.m in Sources */, + 8C009766BC90CAF781042636 /* PageCreationViewController.m in Sources */, + DCCEB3FED1802C0D9C21AB11 /* PageManagerViewController.m in Sources */, + F3F6D205C6FBA36CB262A28E /* SettingsInfo.m in Sources */, + 380469787621147052854E8F /* SettingsViewController.m in Sources */, + B8AA2B793BE43710EFC9D4DC /* TestModule.mm in Sources */, + 74C51526435B1AB788CBF991 /* TurboBaseModule.mm in Sources */, + 72328C3389BA8DD5E54D7D5F /* TurboConfig.m in Sources */, + BD5196B65EE0B712FDA2EB9B /* UIViewController+Title.m in Sources */, + 16D159EB8DCA407DFE4008C8 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A19596015FBB918148A61FF4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DB34DDF95B8E9C9AD87FFE4 /* Pods-HippyDemo.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = HippyDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "${inherited}"; + PRODUCT_BUNDLE_IDENTIFIER = com.tencent.HippyDemo2.db; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BDE0917765AAB8F06FED1216 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + EDA31BB6FAF111A6FD9A3EC9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F2EFFA7A058FDD9A908F1C9C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 331F0D867F0DF450A84E4587 /* Pods-HippyDemo.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = HippyDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "${inherited}"; + PRODUCT_BUNDLE_IDENTIFIER = com.tencent.HippyDemo2.db; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 20BB0794C4C1CBC0A34760F4 /* Build configuration list for PBXNativeTarget "HippyDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2EFFA7A058FDD9A908F1C9C /* Debug */, + A19596015FBB918148A61FF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + BD5ED1CB5E91D88413261758 /* Build configuration list for PBXProject "HippyDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDE0917765AAB8F06FED1216 /* Debug */, + EDA31BB6FAF111A6FD9A3EC9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 215A0FDFFBD7838975355873 /* Project object */; +} diff --git a/framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..919434a6254 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme b/framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme new file mode 100644 index 00000000000..d063a92dfa1 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/examples/ios-demo/README.md b/framework/examples/ios-demo/README.md index 136ba04ba62..a6f68621c51 100644 --- a/framework/examples/ios-demo/README.md +++ b/framework/examples/ios-demo/README.md @@ -1,9 +1,11 @@ -> You need to install cocoapods, cmake and xcodegen before starting HippyDemo +# iOS Demo Running Guide + +> You need to install cocoapods and cmake before starting HippyDemo 1.open your Terminal 2.change the current working folder to framework/examples/ios-demo -3.execute 'xcodegen' command to install dependencies +3.execute 'pod install' command to install dependencies and generate 'HippyDemo.xcworkspace' -4.open 'HippyDemo.xcworkspace' \ No newline at end of file +4.open 'HippyDemo.xcworkspace', run demo! diff --git a/framework/examples/ios-demo/project.yml b/framework/examples/ios-demo/project.yml deleted file mode 100644 index 5cce0a47c01..00000000000 --- a/framework/examples/ios-demo/project.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: HippyDemo -options: - createIntermediateGroups: true - bundleIdPrefix: "com.tencent" - xcodeVersion: "10.0" - postGenCommand: pod update -targets: - HippyDemo: - type: application - platform: iOS - deploymentTarget: "11.0" - settings: - OTHER_LDFLAGS: "${inherited}" - TARGETED_DEVICE_FAMILY: "1" - CLANG_CXX_LANGUAGE_STANDARD: "c++17" - PRODUCT_BUNDLE_IDENTIFIER: "com.tencent.HippyDemo2.db" - sources: - - path: HippyDemo - - path: Assets.xcassets - - path: fonts - buildPhase: - copyFiles: - destination: "resources" - - path: res - type: "folder" From a1d5220b94362b556706cdfbba4d27d3791b8f07 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Thu, 9 May 2024 14:03:37 +0800 Subject: [PATCH 26/28] refactor(ios): non-standard url support of bundle loading --- framework/ios/base/bridge/HippyBridge.mm | 11 ++++- tests/ios/HippyBridgeTest.mm | 63 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/ios/HippyBridgeTest.mm diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index ca43ee33668..d175747632a 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -548,7 +548,16 @@ - (void)loadBundleURL:(NSURL *)bundleURL } return; } - HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", + + // bundleURL checking + NSURLComponents *components = [NSURLComponents componentsWithURL:bundleURL resolvingAgainstBaseURL:NO]; + if (components.scheme == nil) { + // If a given url has no scheme, it is considered a file url by default. + components.scheme = @"file"; + bundleURL = components.URL; + } + + HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); [_bundleURLs addObject:bundleURL]; diff --git a/tests/ios/HippyBridgeTest.mm b/tests/ios/HippyBridgeTest.mm new file mode 100644 index 00000000000..72c7adfb4a6 --- /dev/null +++ b/tests/ios/HippyBridgeTest.mm @@ -0,0 +1,63 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +@interface HippyBridgeTest : XCTestCase + +@end + +@implementation HippyBridgeTest + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testLoadBundleURL { + HippyBridge *bridge = [[HippyBridge alloc] initWithDelegate:nil moduleProvider:nil launchOptions:nil executorKey:nil]; + NSString *testNoSchemePath = @"/Users/ray/testNoSchemePath"; + NSURL *testUrl = [NSURL URLWithString:testNoSchemePath]; + XCTAssert(testUrl.scheme == nil); + [bridge loadBundleURL:testUrl + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) {}]; + NSURL *loadedUrl = bridge.bundleURLs.lastObject; + XCTAssert(loadedUrl.scheme != nil); + XCTAssertTrue(loadedUrl.isFileURL); + + testUrl = [NSURL URLWithString:@"http://hippyjs_no_exist.org"]; + XCTAssert([testUrl.scheme isEqualToString:@"http"]); + [bridge loadBundleURL:testUrl + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) {}]; + loadedUrl = bridge.bundleURLs.lastObject; + XCTAssert([loadedUrl.scheme isEqualToString:@"http"]); + XCTAssertFalse(loadedUrl.isFileURL); +} + + +@end From 3a3364c384a26c9baac2616a4d6c1c5a45a68158 Mon Sep 17 00:00:00 2001 From: siguangli Date: Fri, 17 May 2024 19:38:50 +0800 Subject: [PATCH 27/28] feat(android): add orientationChange event for Modal (#3861) Co-authored-by: siguangli --- docs/api/hippy-react/components.md | 2 +- .../src/components/Modal/index.jsx | 3 +- .../components/native-demos/demo-dialog.vue | 5 + .../components/native-demo/demo-dialog.vue | 6 +- .../views/modal/HippyModalDialogView.java | 98 +++++++++++++++++++ .../hippy/views/modal/HippyModalHostView.java | 6 +- .../tencent/renderer/utils/EventUtils.java | 2 + 7 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java diff --git a/docs/api/hippy-react/components.md b/docs/api/hippy-react/components.md index 5a4954c9641..a7985c317cd 100644 --- a/docs/api/hippy-react/components.md +++ b/docs/api/hippy-react/components.md @@ -182,7 +182,7 @@ import icon from './qb_icon_new.png'; | autoHideStatusBar | 是否在`Modal`显示时自动隐藏状态栏。Android 中仅 api28 以上生效。 `default: false` | `boolean` | `Android` | | autoHideNavigationBar | 是否在`Modal`显示时自动隐藏导航栏。 `default: false` | `boolean` | `Android` | | onShow | 在`Modal`显示时会执行此回调函数。 | `Function` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | -| onOrientationChange | 屏幕旋转方向改变时执行会回调 | `Function` | `Android、iOS` | +| onOrientationChange | 屏幕旋转方向改变时执行会回调,返回当前屏幕显示方向 `{ orientation: portrait|landscape }` | `Function` | `Android、iOS` | | onRequestClose | 在 `Modal` 请求关闭时会执行此回调函数,一般时在 Android 系统里按下硬件返回按钮时触发,一般要在里面处理关闭弹窗。 | `Function` | `Android、hippy-react-web、Voltron` | | transparent | 背景是否是透明的。`default: true` | `boolean` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | visible | 是否显示。`default: true` | `boolean` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | diff --git a/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx b/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx index 5bf6a3fb443..7b2f772c5d3 100644 --- a/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx +++ b/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx @@ -154,7 +154,8 @@ export default class ModalExpo extends React.Component { transparent={true} animationType={this.state.animationType} visible={visible} - onRequestClose={() => { /* Trigger when hardware back pressed */ }} + requestClose={() => { /* Trigger when hardware back pressed */ }} + orientationChange={(evt) => { console.log('orientation changed', evt.orientation); }} supportedOrientations={['portrait']} immersionStatusBar={this.state.immerseStatusBar} autoHideStatusBar={this.state.hideStatusBar} diff --git a/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue b/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue index 62861904c5e..864c1edee6a 100644 --- a/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue +++ b/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue @@ -51,6 +51,7 @@ :autoHideNavigationBar="autoHideNavigationBar" @show="onShow" @requestClose="onClose" + @orientationChange="onOrientationChange" >
@@ -80,6 +81,7 @@ :autoHideStatusBar="autoHideStatusBar" :autoHideNavigationBar="autoHideNavigationBar" @requestClose="onClose" + @orientationChange="onOrientationChange" >
@@ -78,6 +79,7 @@ :animationType="dialogAnimationType" :transparent="true" @requestClose="onClose" + @orientationChange="onOrientationChange" >
{ console.log('Dialog is opening'); }; - + const onOrientationChange = (evt) => { + console.log('orientation changed', evt.nativeParams); + }; const onClose = (evt) => { evt.stopPropagation(); /** diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java new file mode 100644 index 00000000000..75aeb646bca --- /dev/null +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java @@ -0,0 +1,98 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.tencent.mtt.hippy.views.modal; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static com.tencent.renderer.utils.EventUtils.EVENT_ORIENTATION_CHANGED; + +import android.app.Dialog; +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import com.tencent.renderer.utils.EventUtils; +import java.lang.ref.WeakReference; +import java.util.HashMap; + +public class HippyModalDialogView extends Dialog { + private int mOrientation = ORIENTATION_UNDEFINED; + private final ConfigurationChangedListener mListener = new ConfigurationChangedListener();; + private final WeakReference mHostView; + + public HippyModalDialogView(@NonNull Context context, int themeResId, @NonNull View hostView) { + super(context, themeResId); + mHostView = new WeakReference<>(hostView); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Configuration configuration = getContext().getResources().getConfiguration(); + mOrientation = configuration.orientation; + } + + @Override + protected void onStart() { + super.onStart(); + getContext().registerComponentCallbacks(mListener); + } + + @Override + protected void onStop() { + super.onStop(); + getContext().unregisterComponentCallbacks(mListener); + } + + protected void sendOrientationChangeEvent(int orientation) { + final View hostView = mHostView.get(); + if (hostView != null) { + String value; + switch (orientation) { + case ORIENTATION_PORTRAIT: + value = "portrait"; + break; + case ORIENTATION_LANDSCAPE: + value = "landscape"; + break; + default: + value = ""; + } + HashMap params = new HashMap<>(); + params.put("orientation", value); + EventUtils.sendComponentEvent(hostView, EVENT_ORIENTATION_CHANGED, params); + } + } + + private class ConfigurationChangedListener implements ComponentCallbacks { + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (newConfig.orientation != mOrientation) { + sendOrientationChangeEvent(newConfig.orientation); + mOrientation = newConfig.orientation; + } + } + + @Override + public void onLowMemory() { + // Handle low memory event + } + } +} diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java index e7bb9ef9975..de6e82e72f9 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java @@ -70,7 +70,7 @@ public enum AnimationStyleTheme { @NonNull private final DialogRootView mDialogRootView; @Nullable - private Dialog mDialog; + private HippyModalDialogView mDialog; @Nullable private View mContentView; @Nullable @@ -356,9 +356,9 @@ protected void showOrUpdate() { } @NonNull - protected Dialog createDialog(@NonNull Context context) { + protected HippyModalDialogView createDialog(@NonNull Context context) { int themeResId = android.R.style.Theme_Translucent_NoTitleBar; - return new Dialog(context, themeResId); + return new HippyModalDialogView(context, themeResId, this); } @NonNull diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java b/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java index a6da91aac85..4b2ff6ee965 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java @@ -77,6 +77,8 @@ public class EventUtils { public static final String EVENT_MODAL_REQUEST_CLOSE = "requestClose"; // On modal view show. public static final String EVENT_MODAL_SHOW = "show"; + // On modal orientation changed. + public static final String EVENT_ORIENTATION_CHANGED = "orientationChange"; // On refresh wrapper view refresh. public static final String EVENT_REFRESH_WRAPPER_REFRESH = "refresh"; From 60fd4eb13ec85e6de25fe8786737ebc5bc211ae4 Mon Sep 17 00:00:00 2001 From: zealotchen <92966734+zealotchen0@users.noreply.github.com> Date: Mon, 20 May 2024 10:52:27 +0800 Subject: [PATCH 28/28] fix(devtool): fix muli devtool instance crash on android (#3862) * fix(devtool): fix muli devtool instance crash on android * fix(devtool): modify some comment --- driver/js/include/driver/js_driver_utils.h | 3 ++- driver/js/src/js_driver_utils.cc | 6 +++++- .../src/main/cpp/include/connector/js_driver_jni.h | 3 ++- .../driver/js/src/main/cpp/src/js_driver_jni.cc | 7 ++++--- .../main/java/com/openhippy/connector/JsDriver.java | 6 +++--- .../tencent/mtt/hippy/HippyEngineManagerImpl.java | 2 +- .../com/tencent/mtt/hippy/bridge/HippyBridge.java | 2 +- .../tencent/mtt/hippy/bridge/HippyBridgeImpl.java | 9 +++++---- .../mtt/hippy/bridge/HippyBridgeManager.java | 2 +- .../mtt/hippy/bridge/HippyBridgeManagerImpl.java | 13 ++++++++++--- .../com/tencent/mtt/hippy/utils/DevtoolsUtil.java | 8 ++++++-- 11 files changed, 40 insertions(+), 21 deletions(-) diff --git a/driver/js/include/driver/js_driver_utils.h b/driver/js/include/driver/js_driver_utils.h index 7a9a36cad04..188889c5a83 100644 --- a/driver/js/include/driver/js_driver_utils.h +++ b/driver/js/include/driver/js_driver_utils.h @@ -46,7 +46,8 @@ class JsDriverUtils { static std::shared_ptr CreateEngineAndAsyncInitialize(const std::shared_ptr& task_runner, const std::shared_ptr& param, - int64_t group_id); + int64_t group_id, + bool is_reload); static void InitInstance(const std::shared_ptr& engine, const std::shared_ptr& param, diff --git a/driver/js/src/js_driver_utils.cc b/driver/js/src/js_driver_utils.cc index f293886274e..e3b91d4d746 100644 --- a/driver/js/src/js_driver_utils.cc +++ b/driver/js/src/js_driver_utils.cc @@ -133,13 +133,17 @@ void AsyncInitializeEngine(const std::shared_ptr& engine, std::shared_ptr JsDriverUtils::CreateEngineAndAsyncInitialize(const std::shared_ptr& task_runner, const std::shared_ptr& param, - int64_t group_id) { + int64_t group_id, + bool is_reload) { FOOTSTONE_DCHECK(group_id >= -1) << "group_id must be greater than or equal to -1"; std::shared_ptr engine = nullptr; auto group = group_id; if (param->is_debug) { group = VM::kDebuggerGroupId; } + // 1. 调试模式启动新实例不复用V8 context, 调试模式 reload 场景复用 V8 context + // 2. Demo 场景, group_id 为 -1,不复用 V8 context + if ((group == VM::kDebuggerGroupId && is_reload) || (group != VM::kDebuggerGroupId && group != VM::kDefaultGroupId)) { std::lock_guard lock(engine_mutex); auto it = reuse_engine_map.find(group); diff --git a/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h b/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h index 97cb14f0644..56a3d914d87 100644 --- a/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h +++ b/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h @@ -40,7 +40,8 @@ jint CreateJsDriver(JNIEnv* j_env, jint j_dom_manager_id, jobject j_vm_init_param, jint j_vfs_id, - jint j_devtools_id); + jint j_devtools_id, + jboolean j_is_reload); void DestroyJsDriver(JNIEnv* j_env, jobject j_object, diff --git a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc index b3f03d58564..a6891e59f47 100644 --- a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc +++ b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc @@ -71,7 +71,7 @@ inline namespace driver { REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) "onCreate", "([BZZZLcom/openhippy/connector/NativeCallback;" - "JILcom/openhippy/connector/JsDriver$V8InitParams;II)I", + "JILcom/openhippy/connector/JsDriver$V8InitParams;IIZ)I", CreateJsDriver) REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) @@ -266,7 +266,8 @@ jint CreateJsDriver(JNIEnv* j_env, jint j_dom_manager_id, jobject j_vm_init_param, jint j_vfs_id, - jint j_devtools_id) { + jint j_devtools_id, + jboolean j_is_reload) { FOOTSTONE_LOG(INFO) << "CreateJsDriver begin, j_single_thread_mode = " << static_cast(j_single_thread_mode) << ", j_bridge_param_json = " @@ -343,7 +344,7 @@ jint CreateJsDriver(JNIEnv* j_env, } }; auto engine = JsDriverUtils::CreateEngineAndAsyncInitialize( - dom_task_runner, param, static_cast(j_group_id)); + dom_task_runner, param, static_cast(j_group_id), static_cast(j_is_reload)); { std::lock_guard lock(holder_mutex); engine_holder[engine.get()] = engine; diff --git a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java index d61b10eb0c8..6e277584bcb 100644 --- a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java +++ b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java @@ -92,9 +92,9 @@ public void onResourceReady(ByteBuffer output, long resId) { public void initialize(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, - long groupId, int domManagerId, V8InitParams v8InitParams, int vfsId, int devtoolsId) { + long groupId, int domManagerId, V8InitParams v8InitParams, int vfsId, int devtoolsId, boolean isReload) { mInstanceId = onCreate(globalConfig, useLowMemoryMode, enableV8Serialization, - isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId); + isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId, isReload); } public void onDestroy(boolean useLowMemoryMode, boolean isReload, @@ -147,7 +147,7 @@ public void attachToRoot(@NonNull View root) { private native int onCreate(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, - long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId); + long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId, boolean isReload); private native void onDestroy(int instanceId, boolean useLowMemoryMode, boolean isReload, NativeCallback callback); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java index 5543e0957c1..f78739eaaf0 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java @@ -646,7 +646,7 @@ public void callback(Boolean result, Throwable e) { notifyEngineInitialized(EngineInitStatus.STATUS_WRONG_STATE, e); } } - }); + }, onReLoad); } /** diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java index 47cef1a1258..bacc8bcb1b8 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java @@ -27,7 +27,7 @@ public interface HippyBridge { String URI_SCHEME_ASSETS = "asset:"; String URI_SCHEME_FILE = "file:"; - void initJSBridge(String gobalConfig, NativeCallback callback, int groupId); + void initJSBridge(String gobalConfig, NativeCallback callback, int groupId, boolean isReload); boolean runScriptFromUri(String uri, AssetManager assetManager, boolean canUseCodeCache, String codeCacheTag, NativeCallback callback); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java index be142d2d3f6..64e0be25713 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java @@ -91,12 +91,12 @@ public HippyBridgeImpl(HippyEngineContext engineContext, BridgeCallback callback } @Override - public void initJSBridge(String globalConfig, NativeCallback callback, final int groupId) { + public void initJSBridge(String globalConfig, NativeCallback callback, final int groupId, boolean isReload) { mDebugGlobalConfig = globalConfig; - initJSEngine(groupId, callback); + initJSEngine(groupId, callback, isReload); } - private void initJSEngine(int groupId, NativeCallback callback) { + private void initJSEngine(int groupId, NativeCallback callback, boolean isReload) { synchronized (HippyBridgeImpl.class) { try { String localCachePath = mContext.getGlobalConfigs().getContext().getCacheDir() @@ -112,7 +112,8 @@ private void initJSEngine(int groupId, NativeCallback callback) { mContext.getDomManagerId(), mV8InitParams, mContext.getVfsId(), - mContext.getDevtoolsId() + mContext.getDevtoolsId(), + isReload ); mInit = true; } catch (Throwable e) { diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java index 4a586f22c1d..ef10a281273 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java @@ -26,7 +26,7 @@ @SuppressWarnings({"deprecation", "unused"}) public interface HippyBridgeManager { - void initBridge(Callback callback); + void initBridge(Callback callback, boolean isReload); void runBundle(int id, HippyBundleLoader loader); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java index b1b854b1829..72a0af5297d 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java @@ -51,6 +51,7 @@ import com.tencent.mtt.hippy.utils.TimeMonitor; import java.lang.ref.WeakReference; +import java.util.HashMap; import org.json.JSONObject; import java.nio.ByteBuffer; @@ -87,6 +88,9 @@ public enum BridgeState { public static final int DESTROY_CLOSE = 0; public static final int DESTROY_RELOAD = 1; + public static final int CREATE_NORMAL = 0; + public static final int CREATE_RELOAD = 1; + final HippyEngineContext mContext; final HippyBundleLoader mCoreBundleLoader; private final HippyBridge mHippyBridge; @@ -229,6 +233,7 @@ public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_CODE_INIT_BRIDGE: { @SuppressWarnings("unchecked") final com.tencent.mtt.hippy.common.Callback callback = (com.tencent.mtt.hippy.common.Callback) msg.obj; + final int code = msg.arg1; try { mHippyBridge.initJSBridge(getGlobalConfigs(), new NativeCallback(mHandler) { @Override @@ -280,7 +285,7 @@ public void Call(long result, Message message, callback.callback(true, null); } } - }, mGroupId); + }, mGroupId, code == CREATE_RELOAD); } catch (Throwable e) { mBridgeState = BridgeState.UNINITIALIZED; callback.callback(false, e); @@ -361,9 +366,11 @@ public void Call(long result, Message message, String action, } @Override - public void initBridge(Callback callback) { + public void initBridge(Callback callback, boolean isReload) { mHandler = new Handler(mContext.getThreadExecutor().getBridgeThread().getLooper(), this); - Message message = mHandler.obtainMessage(MSG_CODE_INIT_BRIDGE, callback); + Message message = mHandler.obtainMessage(MSG_CODE_INIT_BRIDGE); + message.arg1 = isReload ? CREATE_RELOAD : CREATE_NORMAL; + message.obj = callback; mHandler.sendMessage(message); } diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java index 8217a164977..2a32858bfac 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java @@ -213,9 +213,10 @@ public static void getScreenShot(@NonNull List params, @NonNull final View view, Window window = ((Activity) ((ContextWrapper) context).getBaseContext()).getWindow(); final Bitmap finalBitmap = bitmap; final float finalScale = scale; - PixelCopy.request(window, + try { + PixelCopy.request(window, new Rect(location[0], location[1], location[0] + view.getWidth(), - location[1] + view.getHeight()), + location[1] + view.getHeight()), finalBitmap, new OnPixelCopyFinishedListener() { @Override @@ -227,6 +228,9 @@ public void onPixelCopyFinished(int copyResult) { } } }, new Handler(Looper.getMainLooper())); + } catch (IllegalArgumentException e) { + LogUtils.e(TAG, " PixelCopy.request error", e); + } } else { LogUtils.e(TAG, "getScreenShot context.getBaseContext() is not activity"); }