提供注册组件布局和绘制完成回调通知的能力

Signed-off-by: huangdong57 <huangdong57@huawei.com>
This commit is contained in:
huangdong57 2023-05-27 15:15:12 +08:00
parent 30609e7371
commit 28f080ddbb
6 changed files with 588 additions and 5 deletions

View File

@ -49,6 +49,25 @@ class Mediaquery {
}
}
class UIInspector {
/**
* Construct new instance of ArkUIInspector.
* initialzie with instanceId.
* @param instanceId obtained on the c++ side.
* @since 10
*/
constructor(instanceId) {
this.instanceId_ = instanceId;
this.ohos_UIInspector = globalThis.requireNapi('arkui.inspector');
}
createComponentObserver(id) {
__JSScopeUtil__.syncInstanceId(this.instanceId_);
let componentObserver = this.ohos_UIInspector.createComponentObserver(id);
__JSScopeUtil__.restoreInstanceId();
return componentObserver;
}
}
class UIContext {
/**
* Construct new instance of UIContext.
@ -88,6 +107,11 @@ class UIContext {
return this.mediaquery_;
}
getUIInspector(){
this.UIInspector_ = new UIInspector(this.instanceId_);
return this.UIInspector_;
}
animateTo(value, event) {
__JSScopeUtil__.syncInstanceId(this.instanceId_);
Context.animateTo(value, event);

View File

@ -16,6 +16,7 @@
#ifndef FOUNDATION_ACE_FRAMEWORKS_BRIDGE_JS_FRONTEND_ENGINE_COMMON_JS_ENGINE_H
#define FOUNDATION_ACE_FRAMEWORKS_BRIDGE_JS_FRONTEND_ENGINE_COMMON_JS_ENGINE_H
#include <set>
#include <string>
#include <unordered_map>
@ -28,7 +29,6 @@ class NativeReference;
class NativeValue;
namespace OHOS::Ace::Framework {
class JsAcePage;
using PixelMapNapiEntry = void* (*)(void*, void*);
struct JsModule {
@ -60,6 +60,23 @@ protected:
NativeEngine* nativeEngine_ = nullptr;
};
using InspectorFunc = std::function<void()>;
class InspectorEvent : public virtual AceType {
DECLARE_ACE_TYPE(InspectorEvent, AceType)
public:
explicit InspectorEvent(InspectorFunc&& callback) : callback_(std::move(callback)) {}
~InspectorEvent() override = default;
void operator()() const
{
if (callback_) {
callback_();
}
}
private:
InspectorFunc callback_;
};
class JsEngine : public AceType {
DECLARE_ACE_TYPE(JsEngine, AceType);
@ -96,7 +113,7 @@ public:
}
// Load the js file of the page in NG structure..
virtual bool LoadPageSource(const std::string& /*url*/,
virtual bool LoadPageSource(const std::string& /* url */,
const std::function<void(const std::string&, int32_t)>& errorCallback = nullptr)
{
return false;
@ -178,6 +195,26 @@ public:
}
}
void LayoutInspectorCallback(const std::string& componentId)
{
auto iter = layoutEvents_.find(componentId);
if (iter != layoutEvents_.end()) {
for (auto&& observer : iter->second) {
(*observer)();
}
}
}
void DrawInspectorCallback(const std::string& componentId)
{
auto iter = drawEvents_.find(componentId);
if (iter != drawEvents_.end()) {
for (auto&& observer : iter->second) {
(*observer)();
}
}
}
virtual void RequestAnimationCallback(const std::string& callbackId, uint64_t timeStamp) = 0;
virtual void JsCallback(const std::string& callbackId, const std::string& args) = 0;
@ -271,6 +308,42 @@ public:
mediaUpdateCallback_ = nullptr;
}
void ACE_EXPORT RegisterLayoutInspectorCallback(
const RefPtr<InspectorEvent>& layoutEvent, const std::string& componentId)
{
layoutEvents_[componentId].emplace(layoutEvent);
}
void ACE_EXPORT UnregisterLayoutInspectorCallback(
const RefPtr<InspectorEvent>& layoutEvent, const std::string& componentId)
{
auto iter = layoutEvents_.find(componentId);
if (iter != layoutEvents_.end()) {
iter->second.erase(layoutEvent);
if (iter->second.empty()) {
layoutEvents_.erase(componentId);
}
}
}
void ACE_EXPORT RegisterDrawInspectorCallback(
const RefPtr<InspectorEvent>& drawEvent, const std::string& componentId)
{
drawEvents_[componentId].emplace(drawEvent);
}
void ACE_EXPORT UnregisterDrawInspectorCallback(
const RefPtr<InspectorEvent>& drawEvent, const std::string& componentId)
{
auto iter = drawEvents_.find(componentId);
if (iter != drawEvents_.end()) {
iter->second.erase(drawEvent);
if (iter->second.empty()) {
drawEvents_.erase(componentId);
}
}
}
virtual void RunNativeEngineLoop();
virtual void SetPluginBundleName(const std::string& pluginBundleName) {}
@ -280,7 +353,6 @@ public:
#if !defined(PREVIEW)
static PixelMapNapiEntry GetPixelMapNapiEntry();
#endif
#if defined(PREVIEW)
virtual RefPtr<Component> GetNewComponentWithJsCode(const std::string& jsCode, const std::string& viewID)
{
@ -304,7 +376,6 @@ public:
LOGE("Ark does not support InitializeModuleSearcher");
}
#endif
virtual void FlushReload() {}
virtual NativeValue* GetContextValue()
{
@ -314,6 +385,8 @@ public:
protected:
NativeEngine* nativeEngine_ = nullptr;
std::function<void(JsEngine*)> mediaUpdateCallback_;
std::map<std::string, std::set<RefPtr<InspectorEvent>>> layoutEvents_;
std::map<std::string, std::set<RefPtr<InspectorEvent>>> drawEvents_;
bool needUpdate_ = false;
private:
@ -327,6 +400,5 @@ private:
std::unordered_map<std::string, void*> extraNativeObject_;
};
} // namespace OHOS::Ace::Framework
#endif // FOUNDATION_ACE_FRAMEWORKS_BRIDGE_JS_FRONTEND_ENGINE_COMMON_JS_ENGINE_H

View File

@ -0,0 +1,72 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# 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("//build/ohos.gni")
import("//foundation/arkui/ace_engine/ace_config.gni")
template("napi_inspector_static") {
forward_variables_from(invoker, "*")
ohos_source_set(target_name) {
defines += invoker.defines
cflags_cc += invoker.cflags_cc
if (is_mac) {
cflags_cc += [ "-O0" ]
}
include_dirs = [
"$ace_root",
"$ace_root/frameworks",
"$root_out_dir/arkui/framework",
]
sources = [ "js_inspector.cpp" ]
deps = []
external_deps = []
if (platform != "" && is_cross_platform_build) {
deps += [ "$crossplatform_plugin_root/libs/napi:napi_$platform" ]
} else {
external_deps = [ "napi:ace_napi" ]
}
if (defined(config.libace_target)) {
deps += [ config.libace_target ]
}
subsystem_name = ace_engine_subsystem
part_name = ace_engine_part
}
}
foreach(item, ace_platforms) {
napi_inspector_static("inspector_static_" + item.name) {
defines = []
cflags_cc = []
platform = item.name
config = {
}
if (defined(item.config)) {
config = item.config
}
if (defined(config.defines)) {
defines = config.defines
}
if (defined(config.cflags_cc)) {
cflags_cc = config.cflags_cc
}
}
}

View File

@ -0,0 +1,330 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* 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.
*/
#include "js_inspector.h"
#include <algorithm>
#include "napi/native_common.h"
#include "napi/native_node_api.h"
#include "base/log/log.h"
#include "base/memory/referenced.h"
namespace OHOS::Ace::Napi {
namespace {
constexpr size_t STR_BUFFER_SIZE = 1024;
constexpr uint8_t PARA_COUNT = 2;
} // namespace
static ComponentObserver* GetObserver(napi_env env, napi_value thisVar)
{
ComponentObserver* observer = nullptr;
napi_unwrap(env, thisVar, (void**)&observer);
if (observer->thisVarRef_ == nullptr) {
observer->Initialize(env, thisVar);
}
return observer;
}
static size_t ParseArgs(
napi_env& env, napi_callback_info& info, napi_value& thisVar, napi_value& cb, CalloutType& calloutType)
{
const size_t argNum = 2;
size_t argc = argNum;
napi_value argv[argNum] = { 0 };
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
NAPI_ASSERT_BASE(env, argc > 0, "too few parameter", 0);
napi_valuetype napiType;
NAPI_CALL_BASE(env, napi_typeof(env, argv[0], &napiType), 0);
NAPI_ASSERT_BASE(env, napiType == napi_string, "parameter 1 should be string", 0);
char type[STR_BUFFER_SIZE] = { 0 };
size_t len = 0;
napi_get_value_string_utf8(env, argv[0], type, STR_BUFFER_SIZE, &len);
NAPI_ASSERT_BASE(env, len < STR_BUFFER_SIZE, "condition string too long", 0);
NAPI_ASSERT_BASE(
env, (strcmp("layout", type) == 0 || strcmp("draw", type) == 0), "type mismatch('layout' or 'draw')", 0);
if (strcmp("layout", type) == 0) {
calloutType = CalloutType::LAYOUTCALLOUT;
} else if (strcmp("draw", type) == 0) {
calloutType = CalloutType::DRAWCALLOUT;
} else {
calloutType = CalloutType::UNKNOW;
}
if (argc <= 1) {
return argc;
}
NAPI_CALL_BASE(env, napi_typeof(env, argv[1], &napiType), 0);
NAPI_ASSERT_BASE(env, napiType == napi_function, "type mismatch for parameter 2", 0);
cb = argv[1];
return argc;
}
void ComponentObserver::callUserFunction(std::list<napi_ref>& cbList)
{
for (auto& cbRef : cbList) {
napi_handle_scope scope = nullptr;
napi_open_handle_scope(env_, &scope);
if (scope == nullptr) {
return;
}
napi_value thisVal = nullptr;
napi_get_reference_value(env_, thisVarRef_, &thisVal);
napi_value cb = nullptr;
napi_get_reference_value(env_, cbRef, &cb);
napi_value resultArg = nullptr;
napi_value result = nullptr;
napi_call_function(env_, thisVal, cb, 1, &resultArg, &result);
napi_close_handle_scope(env_, scope);
}
}
std::list<napi_ref>::iterator ComponentObserver::FindCbList(napi_value cb, CalloutType calloutType)
{
if (calloutType == CalloutType::LAYOUTCALLOUT) {
return std::find_if(cbLayoutList_.begin(), cbLayoutList_.end(), [env = env_, cb](const napi_ref& item) -> bool {
bool result = false;
napi_value refItem;
napi_get_reference_value(env, item, &refItem);
napi_strict_equals(env, refItem, cb, &result);
return result;
});
} else {
return std::find_if(cbDrawList_.begin(), cbDrawList_.end(), [env = env_, cb](const napi_ref& item) -> bool {
bool result = false;
napi_value refItem;
napi_get_reference_value(env, item, &refItem);
napi_strict_equals(env, refItem, cb, &result);
return result;
});
}
}
void ComponentObserver::AddCallbackToList(
napi_value cb, std::list<napi_ref>& cbList, CalloutType calloutType, napi_env env, napi_handle_scope scope)
{
auto iter = FindCbList(cb, calloutType);
if (iter != cbList.end()) {
napi_close_handle_scope(env, scope);
return;
}
napi_ref ref = nullptr;
napi_create_reference(env, cb, 1, &ref);
cbList.emplace_back(ref);
napi_close_handle_scope(env, scope);
}
void ComponentObserver::DeleteCallbackFromList(
size_t argc, std::list<napi_ref>& cbList, CalloutType calloutType, napi_value cb, napi_env env)
{
if (argc == 1) {
for (auto& item : cbList) {
napi_delete_reference(env_, item);
}
cbList.clear();
} else {
NAPI_ASSERT_RETURN_VOID(env, (argc == PARA_COUNT && cb != nullptr), "Invalid arguments");
auto iter = FindCbList(cb, calloutType);
if (iter != cbList.end()) {
napi_delete_reference(env_, *iter);
cbList.erase(iter);
}
}
}
void ComponentObserver::FunctionOn(napi_env& env, napi_value result, const char* funName)
{
napi_value funcValue = nullptr;
auto On = [](napi_env env, napi_callback_info info) -> napi_value {
auto jsEngine = EngineHelper::GetCurrentEngine();
if (!jsEngine) {
LOGE("get jsEngine failed");
return nullptr;
}
napi_handle_scope scope = nullptr;
napi_open_handle_scope(env, &scope);
if (scope == nullptr) {
return nullptr;
}
napi_value thisVar = nullptr;
napi_value cb = nullptr;
CalloutType calloutType = CalloutType::UNKNOW;
size_t argc = ParseArgs(env, info, thisVar, cb, calloutType);
NAPI_ASSERT(env, (argc == 2 && thisVar != nullptr && cb != nullptr), "Invalid arguments");
ComponentObserver* observer = GetObserver(env, thisVar);
if (!observer) {
LOGE("observer is null");
napi_close_handle_scope(env, scope);
return nullptr;
}
if (calloutType == CalloutType::LAYOUTCALLOUT) {
observer->AddCallbackToList(cb, observer->cbLayoutList_, calloutType, env, scope);
} else if (calloutType == CalloutType::DRAWCALLOUT) {
observer->AddCallbackToList(cb, observer->cbDrawList_, calloutType, env, scope);
}
return nullptr;
};
napi_create_function(env, funName, NAPI_AUTO_LENGTH, On, nullptr, &funcValue);
napi_set_named_property(env, result, funName, funcValue);
}
void ComponentObserver::FunctionOff(napi_env& env, napi_value result, const char* funName)
{
napi_value funcValue = nullptr;
auto Off = [](napi_env env, napi_callback_info info) -> napi_value {
LOGI("NAPI ComponentObserver off called");
napi_value thisVar = nullptr;
napi_value cb = nullptr;
CalloutType calloutType = CalloutType::UNKNOW;
size_t argc = ParseArgs(env, info, thisVar, cb, calloutType);
ComponentObserver* observer = GetObserver(env, thisVar);
if (!observer) {
LOGE("observer is null");
return nullptr;
}
if (calloutType == CalloutType::LAYOUTCALLOUT) {
LOGI("NAPI ComponentObserver Off called NapiLayoutCallback");
observer->DeleteCallbackFromList(argc, observer->cbLayoutList_, calloutType, cb, env);
} else if (calloutType == CalloutType::DRAWCALLOUT) {
LOGI("NAPI ComponentObserver Off called NapiDrawCallback");
observer->DeleteCallbackFromList(argc, observer->cbDrawList_, calloutType, cb, env);
}
return nullptr;
};
napi_create_function(env, funName, NAPI_AUTO_LENGTH, Off, nullptr, &funcValue);
napi_set_named_property(env, result, funName, funcValue);
}
void ComponentObserver::NapiSerializer(napi_env& env, napi_value& result)
{
napi_create_object(env, &result);
napi_handle_scope scope = nullptr;
napi_open_handle_scope(env, &scope);
if (scope == nullptr) {
return;
}
napi_value componentIdVal = nullptr;
napi_create_string_utf8(env, componentId_.c_str(), componentId_.size(), &componentIdVal);
napi_set_named_property(env, result, "componentId", componentIdVal);
napi_close_handle_scope(env, scope);
napi_wrap(
env, result, this,
[](napi_env env, void* data, void* hint) {
ComponentObserver* observer = static_cast<ComponentObserver*>(data);
if (observer != nullptr) {
delete observer;
}
},
nullptr, nullptr);
FunctionOn(env, result, "on");
FunctionOff(env, result, "off");
}
void ComponentObserver::Initialize(napi_env env, napi_value thisVar)
{
napi_handle_scope scope = nullptr;
napi_open_handle_scope(env, &scope);
if (scope == nullptr) {
return;
}
if (env_ == nullptr) {
env_ = env;
}
napi_create_reference(env, thisVar, 1, &thisVarRef_);
napi_close_handle_scope(env, scope);
}
static napi_value JSCreateComponentObserver(napi_env env, napi_callback_info info)
{
LOGI("napi_value JSCreateComponentObserve");
/* Get arguments */
size_t argc = 1;
napi_value argv = nullptr;
napi_value thisVar = nullptr;
void* data = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data));
NAPI_ASSERT(env, argc == 1, "requires 1 parameter");
/* Checkout arguments */
napi_valuetype type;
NAPI_CALL(env, napi_typeof(env, argv, &type));
NAPI_ASSERT(env, type == napi_string, "type mismatch");
char componentId[STR_BUFFER_SIZE] = { 0 };
size_t len = 0;
napi_get_value_string_utf8(env, argv, componentId, STR_BUFFER_SIZE, &len);
NAPI_ASSERT(env, len < STR_BUFFER_SIZE, "condition string too long");
/* construct object for query */
std::string componentIdStr(componentId, len);
ComponentObserver* observer = new ComponentObserver(componentIdStr);
napi_value result = nullptr;
observer->NapiSerializer(env, result);
auto layoutCallback = [observer]() { observer->callUserFunction(observer->cbLayoutList_); };
observer->layoutEvent_ = AceType::MakeRefPtr<InspectorEvent>(std::move(layoutCallback));
auto drawCallback = [observer]() { observer->callUserFunction(observer->cbDrawList_); };
observer->drawEvent_ = AceType::MakeRefPtr<InspectorEvent>(std::move(drawCallback));
auto jsEngine = EngineHelper::GetCurrentEngine();
if (!jsEngine) {
LOGE("get jsEngine failed");
return nullptr;
}
jsEngine->RegisterLayoutInspectorCallback(observer->layoutEvent_, observer->componentId_);
jsEngine->RegisterDrawInspectorCallback(observer->drawEvent_, observer->componentId_);
#if defined(PREVIEW)
layoutCallback();
drawCallback();
#endif
return result;
}
static napi_value Export(napi_env env, napi_value exports)
{
napi_property_descriptor properties[] = { DECLARE_NAPI_FUNCTION(
"createComponentObserver", JSCreateComponentObserver) };
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties));
return exports;
}
static napi_module inspector_module = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Export,
.nm_modname = "arkui.inspector", // relative to the dynamic library's name
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void Register()
{
napi_module_register(&inspector_module);
}
} // namespace OHOS::Ace::Napi

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* 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.
*/
#ifndef FOUNDATION_ACE_INTERFACE_INNERKITS_INSPECTOR_H
#define FOUNDATION_ACE_INTERFACE_INNERKITS_INSPECTOR_H
#include "napi/native_api.h"
#include "napi/native_common.h"
#include "napi/native_node_api.h"
#include "base/log/log.h"
#include "bridge/common/utils/engine_helper.h"
#include "bridge/js_frontend/engine/common/js_engine.h"
namespace OHOS::Ace::Napi { // namespace
using namespace OHOS::Ace::Framework;
enum class CalloutType {
LAYOUTCALLOUT = 0,
DRAWCALLOUT,
UNKNOW,
};
class ComponentObserver {
public:
explicit ComponentObserver(const std::string& componentId) : componentId_(componentId) {}
~ComponentObserver()
{
if (env_ == nullptr) {
return;
}
for (auto& layoutitem : cbLayoutList_) {
napi_delete_reference(env_, layoutitem);
}
for (auto& drawitem : cbDrawList_) {
napi_delete_reference(env_, drawitem);
}
if (thisVarRef_ != nullptr) {
napi_delete_reference(env_, thisVarRef_);
}
auto jsEngine = EngineHelper::GetCurrentEngine();
if (!jsEngine) {
LOGE("get jsEngine failed");
return;
}
jsEngine->UnregisterLayoutInspectorCallback(layoutEvent_, componentId_);
jsEngine->UnregisterDrawInspectorCallback(drawEvent_, componentId_);
}
void Initialize(napi_env env, napi_value thisVar);
void callUserFunction(std::list<napi_ref>& cbList);
std::list<napi_ref>::iterator FindCbList(napi_value cb, CalloutType calloutType);
void NapiSerializer(napi_env& env, napi_value& result);
void AddCallbackToList(
napi_value cb, std::list<napi_ref>& cbList, CalloutType calloutType, napi_env env, napi_handle_scope scope);
void DeleteCallbackFromList(
size_t argc, std::list<napi_ref>& cbList, CalloutType calloutType, napi_value cb, napi_env env);
RefPtr<InspectorEvent> layoutEvent_;
RefPtr<InspectorEvent> drawEvent_;
std::string componentId_;
std::list<napi_ref> cbLayoutList_;
std::list<napi_ref> cbDrawList_;
napi_ref thisVarRef_ = nullptr;
private:
void FunctionOn(napi_env& env, napi_value result, const char* funName);
void FunctionOff(napi_env& env, napi_value result, const char* funName);
napi_env env_ = nullptr;
};
} // namespace OHOS::Ace::Napi
#endif

View File

@ -17,6 +17,7 @@ import("//foundation/arkui/ace_engine/ace_config.gni")
common_napi_libs = [
# napi .so needs to be all lower case
"arkui/component_snapshot",
"arkui/inspector",
"configuration",
"device",
"font",