Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ios): add fcp monitor support #3803

Merged
merged 3 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading