Skip to content

Commit

Permalink
feat(ios): add fcp monitor support (#3803)
Browse files Browse the repository at this point in the history
* feat(ios): add fcp monitor support

* chore(ios): performance related code optimize

* test(ios): add some test code
  • Loading branch information
wwwcg authored Apr 2, 2024
1 parent 4ac9966 commit 7d29a3c
Show file tree
Hide file tree
Showing 15 changed files with 182 additions and 16 deletions.
2 changes: 2 additions & 0 deletions driver/js/include/driver/performance/performance.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ namespace hippy {
inline namespace driver {
inline namespace performance {

extern const char* kPerfNavigationHippyInit;

class Performance {
public:
using string_view = footstone::string_view;
Expand Down
2 changes: 1 addition & 1 deletion driver/js/src/js_driver_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ bool JsDriverUtils::RunScript(const std::shared_ptr<Scope>& scope,
}

// perfromance start time
auto entry = scope->GetPerformance()->PerformanceNavigation("hippyInit");
auto entry = scope->GetPerformance()->PerformanceNavigation(kPerfNavigationHippyInit);
entry->BundleInfoOfUrl(uri).execute_source_start_ = footstone::TimePoint::SystemNow();

#ifdef JS_V8
Expand Down
1 change: 1 addition & 0 deletions driver/js/src/performance/performance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace hippy {
inline namespace driver {
inline namespace performance {

const char* kPerfNavigationHippyInit = "hippyInit";
constexpr uint32_t kMaxSize = 250;

Performance::Performance(): resource_timing_current_buffer_size_(0),
Expand Down
4 changes: 2 additions & 2 deletions driver/js/src/scope.cc
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ void Scope::RunJS(const string_view& data,
auto callback = [WEAK_THIS, data, uri, name, is_copy, weak_context] {
DEFINE_AND_CHECK_SELF(Scope)
// perfromance start time
auto entry = self->GetPerformance()->PerformanceNavigation("hippyInit");
auto entry = self->GetPerformance()->PerformanceNavigation(kPerfNavigationHippyInit);
entry->BundleInfoOfUrl(uri).execute_source_start_ = footstone::TimePoint::SystemNow();

#ifdef JS_V8
Expand Down Expand Up @@ -526,7 +526,7 @@ void Scope::LoadInstance(const std::shared_ptr<HippyValue>& value) {
#endif
DEFINE_AND_CHECK_SELF(Scope)
// perfromance start time
auto entry = self->GetPerformance()->PerformanceNavigation("hippyInit");
auto entry = self->GetPerformance()->PerformanceNavigation(kPerfNavigationHippyInit);
entry->SetHippyRunApplicationStart(footstone::TimePoint::SystemNow());

std::shared_ptr<Ctx> context = weak_context.lock();
Expand Down
45 changes: 39 additions & 6 deletions framework/ios/base/bridge/HippyBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#import "NativeRenderManager.h"
#import "HippyRootView.h"
#import "UIView+Hippy.h"
#import "UIView+MountEvent.h"


#ifdef ENABLE_INSPECTOR
Expand Down Expand Up @@ -227,6 +228,8 @@ - (instancetype)initWithDelegate:(id<HippyBridgeDelegate>)delegate
registerLogDelegateToHippyCore();
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rootViewContentDidAppear:)
name:HippyContentDidAppearNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFirstContentfulPaintEnd:)
name:HippyFirstContentfulPaintEndNotification object:nil];
HippyExecuteOnMainThread(^{
self->_isOSNightMode = [HippyDeviceBaseInfo isUIScreenInOSDarkMode];
self.cachedDimensionsInfo = hippyExportedDimensions(self);
Expand All @@ -252,11 +255,19 @@ - (instancetype)initWithDelegate:(id<HippyBridgeDelegate>)delegate
- (void)rootViewContentDidAppear:(NSNotification *)noti {
UIView *rootView = [noti object];
if (rootView) {
auto domManager = _javaScriptExecutor.pScope->GetDomManager().lock();
if (domManager) {
auto viewRenderManager = [rootView renderManager];
if (_renderManager == viewRenderManager.lock()) {
auto entry = _javaScriptExecutor.pScope->GetPerformance()->PerformanceNavigation("hippyInit");
auto viewRenderManager = [rootView renderManager];
if (_renderManager && _renderManager == viewRenderManager.lock()) {
std::shared_ptr<hippy::Scope> scope = _javaScriptExecutor.pScope;
if (!scope) {
return;
}
auto domManager = scope->GetDomManager().lock();
auto performance = scope->GetPerformance();
if (domManager && performance) {
auto entry = performance->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
if (!entry) {
return;
}
entry->SetHippyDomStart(domManager->GetDomStartTimePoint());
entry->SetHippyDomEnd(domManager->GetDomEndTimePoint());
entry->SetHippyFirstFrameStart(domManager->GetDomEndTimePoint());
Expand All @@ -266,6 +277,28 @@ - (void)rootViewContentDidAppear:(NSNotification *)noti {
}
}

- (void)onFirstContentfulPaintEnd:(NSNotification *)noti {
UIView *fcpView = [noti object];
if (fcpView) {
auto viewRenderManager = [fcpView renderManager];
if (_renderManager && _renderManager == viewRenderManager.lock()) {
std::shared_ptr<hippy::Scope> scope = _javaScriptExecutor.pScope;
if (!scope) {
return;
}
auto domManager = scope->GetDomManager().lock();
auto performance = scope->GetPerformance();
if (domManager && performance) {
auto entry = performance->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
if (!entry) {
return;
}
entry->SetHippyFirstContentfulPaintEnd(footstone::TimePoint::SystemNow());
}
}
}
}

- (void)dealloc {
/**
* This runs only on the main thread, but crashes the subclass
Expand Down Expand Up @@ -424,7 +457,7 @@ - (void)setUp {
if (strongSelf) {
dispatch_semaphore_signal(strongSelf.moduleSemaphore);
footstone::TimePoint endTime = footstone::TimePoint::SystemNow();
auto enty = strongSelf.javaScriptExecutor.pScope->GetPerformance()->PerformanceNavigation("hippyInit");
auto enty = strongSelf.javaScriptExecutor.pScope->GetPerformance()->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
enty->SetHippyNativeInitStart(strongSelf->_startTime);
enty->SetHippyNativeInitEnd(endTime);
}
Expand Down
4 changes: 2 additions & 2 deletions framework/ios/base/executors/HippyJSExecutor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ - (void)setup {
[strongSelf executeBlockOnJavaScriptQueue:obj];
}];
[strongSelf->_pendingCalls removeAllObjects];
auto entry = scope->GetPerformance()->PerformanceNavigation("hippyInit");
auto entry = scope->GetPerformance()->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
entry->SetHippyJsEngineInitStart(startPoint);
entry->SetHippyJsEngineInitEnd(footstone::TimePoint::SystemNow());
}
Expand Down Expand Up @@ -553,7 +553,7 @@ - (void)executeApplicationScript:(NSData *)script sourceURL:(NSURL *)sourceURL o
return;
}
NSError *error = nil;
auto entry = strongSelf.pScope->GetPerformance()->PerformanceNavigation("hippyInit");
auto entry = strongSelf.pScope->GetPerformance()->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
string_view url = [[sourceURL absoluteString] UTF8String]?:"";
entry->BundleInfoOfUrl(url).execute_source_start_ = footstone::TimePoint::SystemNow();
id result = executeApplicationScript(script, sourceURL, strongSelf.pScope->GetContext(), &error);
Expand Down
2 changes: 1 addition & 1 deletion renderer/native/ios/renderer/HippyComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,5 @@ typedef void (^HippyDirectEventBlock)(NSDictionary *body);
/// Hippy use multiple of 10 as tag of root view
/// - Parameter hippyTag: hippy tag
static inline BOOL HippyIsHippyRootView(NSNumber *hippyTag) {
return hippyTag.integerValue % 10 == 0;
return hippyTag ? hippyTag.integerValue % 10 == 0 : false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view {
HIPPY_EXPORT_VIEW_PROPERTY(onDidUnmount, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(onAttachedToWindow, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(onDetachedFromWindow, HippyDirectEventBlock)
HIPPY_EXPORT_VIEW_PROPERTY(paintType, HippyPaintType)

HIPPY_EXPORT_SHADOW_PROPERTY(zIndex, NSInteger)

Expand Down
5 changes: 3 additions & 2 deletions renderer/native/ios/renderer/component/view/UIView+Hippy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#import "HippyViewEventProtocol.h"

@class HippyShadowView;
@class HippyRootView;

@interface UIView (Hippy) <HippyComponent, HippyViewEventProtocol>

Expand All @@ -39,9 +40,9 @@
- (void)clearSortedSubviews;

/**
* TODO: remove
* Get HippyRootView of current view
*/
- (UIView *)NativeRenderRootView;
- (nullable HippyRootView *)hippyRootView;

/**
* z-index, used to override sibling order in didUpdateHippySubviews.
Expand Down
4 changes: 2 additions & 2 deletions renderer/native/ios/renderer/component/view/UIView+Hippy.mm
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,14 @@ - (void)resetHippySubviews {
[self clearSortedSubviews];
}

- (UIView *)NativeRenderRootView {
- (HippyRootView *)hippyRootView {
UIView *candidateRootView = self;
BOOL isRootView = [candidateRootView isHippyRootView];
while (!isRootView && candidateRootView) {
candidateRootView = [candidateRootView parent];
isRootView = [candidateRootView isHippyRootView];
}
return candidateRootView;
return isRootView ? (HippyRootView *)candidateRootView.superview : nil;
}

- (NSInteger)hippyZIndex {
Expand Down
10 changes: 10 additions & 0 deletions renderer/native/ios/renderer/component/view/UIView+MountEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@

#import <UIKit/UIKit.h>
#import "HippyComponent.h"
#import "HippyConvert+NativeRender.h"

/// FCP Notification
HIPPY_EXTERN const NSNotificationName HippyFirstContentfulPaintEndNotification;


@interface UIView (MountEvent)

/// Paint Type of `View` node
/// Used to calculate rendering time, etc
/// e.g. fcp...
@property (nonatomic, assign) HippyPaintType paintType;

@property (nonatomic, copy) HippyDirectEventBlock onAppear;
@property (nonatomic, copy) HippyDirectEventBlock onDisappear;
@property (nonatomic, copy) HippyDirectEventBlock onWillAppear;
Expand Down
41 changes: 41 additions & 0 deletions renderer/native/ios/renderer/component/view/UIView+MountEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@

#import "UIView+MountEvent.h"
#import "objc/runtime.h"
#import "UIView+Hippy.h"
#import "HippyRootView.h"

/// The FCP Notification Imp
const NSNotificationName HippyFirstContentfulPaintEndNotification = @"HippyFirstContentfulPaintEndNotification";

@interface HippyRootView (PaintEventSupport)

/// Send FCP Notification only for the first time
/// - Parameter fcpView: fcp view
- (void)sendFCPNotiIfNeeded:(UIView *)fcpView;

@end


@implementation UIView (MountEvent)

Expand Down Expand Up @@ -79,6 +93,10 @@ - (void)viewDidUnmoundEvent {
}

- (void)sendAttachedToWindowEvent {
if (HippyPaintTypeFCP == self.paintType) {
HippyRootView *rootView = (HippyRootView *)[self hippyRootView];
[rootView sendFCPNotiIfNeeded:self];
}
if (self.onAttachedToWindow) {
self.onAttachedToWindow(nil);
}
Expand All @@ -90,4 +108,27 @@ - (void)sendDetachedFromWindowEvent {
}
}

#pragma mark -

- (HippyPaintType)paintType {
return [objc_getAssociatedObject(self, @selector(paintType)) integerValue];
}

- (void)setPaintType:(HippyPaintType)paintType {
objc_setAssociatedObject(self, @selector(paintType), @(paintType), OBJC_ASSOCIATION_RETAIN);
}


@end


@implementation HippyRootView (PaintEventSupport)

- (void)sendFCPNotiIfNeeded:(UIView *)fcpView {
if (nil == objc_getAssociatedObject(self, @selector(sendFCPNotiIfNeeded:))) {
objc_setAssociatedObject(self, @selector(sendFCPNotiIfNeeded:), @(YES), OBJC_ASSOCIATION_RETAIN);
[NSNotificationCenter.defaultCenter postNotificationName:HippyFirstContentfulPaintEndNotification object:fcpView];
}
}

@end
14 changes: 14 additions & 0 deletions renderer/native/ios/utils/HippyConvert+NativeRender.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,18 @@ typedef NS_ENUM(NSInteger, HippyTextVerticalAlignType) {

@end


typedef NS_ENUM(NSInteger, HippyPaintType) {
HippyPaintTypeUndefined = 0,
HippyPaintTypeFCP = 1,
};

@interface HippyConvert (HippyPaintType)

/// Convert PaintTypeString to enum
/// - Parameter json: string
+ (HippyPaintType)HippyPaintType:(id)json;

@end

NS_ASSUME_NONNULL_END
8 changes: 8 additions & 0 deletions renderer/native/ios/utils/HippyConvert+NativeRender.m
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,11 @@ @implementation HippyConvert (HippyTextEnumDefines)
}), HippyTextVerticalAlignUndefined, integerValue)

@end

@implementation HippyConvert (HippyPaintType)

HIPPY_ENUM_CONVERTER(HippyPaintType, (@{
@"fcp": @(HippyPaintTypeFCP),
}), HippyPaintTypeUndefined, integerValue)

@end
55 changes: 55 additions & 0 deletions tests/ios/HippyUIViewCategoryTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*!
* 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 <XCTest/XCTest.h>
#import "UIView+Hippy.h"

@interface HippyUIViewCategoryTest : XCTestCase

@end

@implementation HippyUIViewCategoryTest

- (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)testGetHippyRootView {
UIView *testView = [UIView new];
XCTAssertNil([testView hippyRootView]);

testView.hippyTag = @(10);
id testSuperView = [UIView new];
[testSuperView addSubview:testView];
XCTAssert([testView hippyRootView] == testSuperView);
testView.hippyTag = @(11);
XCTAssert([testView hippyRootView] == nil);


}


@end

0 comments on commit 7d29a3c

Please sign in to comment.