* Copyright (c) 2026 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 "dialog_controller.h"
#include "dialog_params.h"
#include "base/error/error_code.h"
#include "base/log/log.h"
#include "base/memory/referenced.h"
#include "base/subwindow/subwindow_manager.h"
#include "bridge/common/utils/engine_helper.h"
#include "core/common/ace_engine.h"
#include "core/common/container.h"
#include "core/common/container_scope.h"
#include "core/components_ng/pattern/overlay/overlay_manager.h"
#include "core/components_ng/pattern/overlay/dialog_manager.h"
#include "core/pipeline_ng/pipeline_context.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"
#include "interfaces/napi/kits/utils/napi_utils.h"
extern const char _binary_dialog_js_start[];
extern const char _binary_dialog_js_end[];
extern const char _binary_dialog_abc_start[];
extern const char _binary_dialog_abc_end[];
extern "C" __attribute__((visibility("default"))) void NAPI_dialog_GetJSCode(const char** buf, int* bufLen)
{
if (buf != nullptr) {
*buf = _binary_dialog_js_start;
}
if (bufLen != nullptr) {
*bufLen = _binary_dialog_js_end - _binary_dialog_js_start;
}
}
extern "C" __attribute__((visibility("default"))) void NAPI_dialog_GetABCCode(const char** buf, int* bufLen)
{
if (buf != nullptr) {
*buf = _binary_dialog_abc_start;
}
if (bufLen != nullptr) {
*bufLen = _binary_dialog_abc_end - _binary_dialog_abc_start;
}
}
namespace OHOS::Ace {
namespace {
constexpr size_t PARAM_INDEX_FIRST = 0;
constexpr size_t PARAM_INDEX_SECOND = 1;
constexpr size_t MAX_PARAM_COUNT = 2;
constexpr size_t MIN_PARAM_COUNT = 1;
}
napi_value JSPresentDialog(napi_env env, napi_callback_info info)
{
size_t argc = MIN_PARAM_COUNT;
napi_value argv[MIN_PARAM_COUNT] = { 0 };
napi_value thisVar = nullptr;
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
auto context = std::make_shared<DialogAsyncContext>();
context->env = env;
context->instanceId = Container::CurrentIdSafely();
napi_value result = CreateDialogPromise(env, context);
if (argc < MIN_PARAM_COUNT) {
RejectPromise(env, context->deferred, "The number of parameters must be greater than or equal to 1.",
ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
DialogProperties dialogProps;
dialogProps.isDialogNapiCall = true;
if (!GetDialogOptions(env, argv[PARAM_INDEX_FIRST], dialogProps)) {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
auto mountCallback = CreatePresentCustomFinishCallback(context);
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
auto container = AceEngine::Get().GetContainer(context->instanceId);
CHECK_NULL_RETURN(container, result);
container->GetTaskExecutor()->PostTask(
[context]() {
if (context == nullptr) {
return;
}
napi_handle_scope scope = nullptr;
napi_open_handle_scope(context->env, &scope);
if (context->deferred) {
RejectPromise(context->env, context->deferred,
"Internal error.", ERROR_CODE_INTERNAL_ERROR);
context->deferred = nullptr;
}
napi_close_handle_scope(context->env, scope);
},
TaskExecutor::TaskType::JS, "ArkUIDialogShowNoDelegate");
context = nullptr;
return result;
}
{
auto instanceId = context->instanceId;
ContainerScope scope(instanceId);
auto pipelineContext = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_RETURN(pipelineContext, result);
auto overlayManager = pipelineContext->GetOverlayManager();
auto task = [dialogProps, instanceId,
callback = std::move(mountCallback)](const RefPtr<NG::OverlayManager>& overlayManager) {
CHECK_NULL_VOID(overlayManager);
auto container = AceEngine::Get().GetContainer(instanceId);
CHECK_NULL_VOID(container);
if (container->IsSubContainer()) {
auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(instanceId);
container = AceEngine::Get().GetContainer(parentContainerId);
CHECK_NULL_VOID(container);
}
if (!dialogProps.isShowInSubWindow) {
overlayManager->ShowDialogWithErrorCallback(
dialogProps, nullptr, AceApplicationInfo::GetInstance().IsRightToLeft(), std::move(callback));
return;
}
auto dialog = SubwindowManager::GetInstance()->ShowDialogNG(dialogProps, nullptr, std::move(callback));
CHECK_NULL_VOID(dialog);
if (dialogProps.isModal && !container->IsUIExtensionWindow()) {
DialogProperties maskProps = {
.autoCancel = dialogProps.autoCancel,
.isMask = true,
};
auto mask = overlayManager->ShowDialog(maskProps, nullptr, false);
CHECK_NULL_VOID(mask);
overlayManager->SetMaskNodeId(dialog->GetId(), mask->GetId());
}
};
if (dialogProps.dialogLevelMode == LevelMode::EMBEDDED) {
NG::DialogManager::ShowInEmbeddedOverlay(
std::move(task), "ArkUIDialogShow", dialogProps.dialogLevelUniqueId);
} else {
pipelineContext->GetTaskExecutor()->PostTask(
[task, weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
task(weak.Upgrade());
},
TaskExecutor::TaskType::UI, "ArkUIDialogShow");
}
}
return result;
}
napi_value JSPresentCustomDialog(napi_env env, napi_callback_info info)
{
size_t argc = MAX_PARAM_COUNT;
napi_value argv[MAX_PARAM_COUNT] = { 0 };
napi_value thisVar = nullptr;
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
auto context = std::make_shared<DialogAsyncContext>();
context->env = env;
context->instanceId = Container::CurrentIdSafely();
napi_value result = CreateDialogPromise(env, context);
if (argc < MIN_PARAM_COUNT) {
RejectPromise(env, context->deferred, "The number of parameters must be greater than or equal to 1.",
ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
DialogProperties dialogProps;
dialogProps.isUserCreatedDialog = true;
dialogProps.isDialogNapiCall = true;
dialogProps.customStyle = false;
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[PARAM_INDEX_FIRST], &valueType);
if (valueType == napi_function) {
GetCustomBuilder(env, argv[PARAM_INDEX_FIRST], dialogProps);
} else if (valueType == napi_object) {
WeakPtr<NG::UINode> nodeWk;
if (!GetFrameNodePtr(env, argv[PARAM_INDEX_FIRST], nodeWk)) {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
dialogProps.contentNode = nodeWk;
} else {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
if (argc >= MAX_PARAM_COUNT && !GetCustomDialogOptions(env, argv[PARAM_INDEX_SECOND], dialogProps)) {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
auto finishCallback = CreatePresentCustomFinishCallback(context);
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
auto container = AceEngine::Get().GetContainer(context->instanceId);
CHECK_NULL_RETURN(container, result);
container->GetTaskExecutor()->PostTask(
[context]() {
if (context == nullptr) {
return;
}
napi_handle_scope scope = nullptr;
napi_open_handle_scope(context->env, &scope);
if (context->deferred) {
RejectPromise(context->env, context->deferred,
"Internal error.", ERROR_CODE_INTERNAL_ERROR);
context->deferred = nullptr;
}
napi_close_handle_scope(context->env, scope);
},
TaskExecutor::TaskType::JS, "ArkUIDialogOpenCustomNoDelegate");
context = nullptr;
return result;
}
{
auto instanceId = context->instanceId;
ContainerScope scope(instanceId);
auto pipelineContext = NG::PipelineContext::GetCurrentContext();
CHECK_NULL_RETURN(pipelineContext, result);
auto overlayManager = pipelineContext->GetOverlayManager();
auto task = [dialogProps, callback = std::move(finishCallback)](
const RefPtr<NG::OverlayManager>& overlayManager) mutable {
CHECK_NULL_VOID(overlayManager);
if (dialogProps.isShowInSubWindow) {
SubwindowManager::GetInstance()->OpenCustomDialogNG(dialogProps, std::move(callback));
if (dialogProps.isModal) {
TAG_LOGW(AceLogTag::ACE_OVERLAY, "Temporary not support isShowInSubWindow and isModal.");
}
} else {
overlayManager->OpenCustomDialogWithErrorCallback(dialogProps, std::move(callback));
}
};
if (dialogProps.dialogLevelMode == LevelMode::EMBEDDED) {
NG::DialogManager::ShowInEmbeddedOverlay(
std::move(task), "ArkUIDialogOpenCustom", dialogProps.dialogLevelUniqueId);
} else {
pipelineContext->GetTaskExecutor()->PostTask(
[task = std::move(task), weak = WeakPtr<NG::OverlayManager>(overlayManager)]() mutable {
task(weak.Upgrade());
},
TaskExecutor::TaskType::UI, "ArkUIDialogOpenCustom");
}
}
return result;
}
napi_value JSUpdateCustomDialog(napi_env env, napi_callback_info info)
{
size_t argc = MAX_PARAM_COUNT;
napi_value argv[MAX_PARAM_COUNT] = { 0 };
napi_value thisVar = nullptr;
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
auto context = std::make_shared<DialogAsyncContext>();
context->env = env;
context->instanceId = Container::CurrentIdSafely();
napi_value result = CreateDialogPromise(env, context);
if (argc < MAX_PARAM_COUNT) {
RejectPromise(env, context->deferred, "The number of parameters must be greater than or equal to 2.",
ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
WeakPtr<NG::UINode> nodeWk;
if (!GetFrameNodePtr(env, argv[PARAM_INDEX_FIRST], nodeWk)) {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
DialogProperties dialogProps;
if (argc >= MAX_PARAM_COUNT && !GetDialogBaseOptions(env, argv[PARAM_INDEX_SECOND], dialogProps)) {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
auto finishCallback = CreateUpdateFinishCallback(context);
auto nodePtr = nodeWk.Upgrade();
CHECK_NULL_RETURN(nodePtr, result);
auto pipelineContext = nodePtr->GetContextWithCheck();
CHECK_NULL_RETURN(pipelineContext, result);
auto overlayManager = pipelineContext->GetOverlayManager();
auto task = [nodeWk, dialogProps, callback = std::move(finishCallback),
weak = WeakPtr<NG::OverlayManager>(overlayManager)]() mutable {
auto overlayManager = weak.Upgrade();
CHECK_NULL_VOID(overlayManager);
overlayManager->UpdateCustomDialog(nodeWk, dialogProps, std::move(callback));
};
pipelineContext->GetTaskExecutor()->PostTask(
std::move(task), TaskExecutor::TaskType::UI, "ArkUIDialogUpdate");
return result;
}
napi_value JSDismissDialog(napi_env env, napi_callback_info info)
{
size_t argc = MIN_PARAM_COUNT;
napi_value argv[MIN_PARAM_COUNT] = { 0 };
napi_value thisVar = nullptr;
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
auto context = std::make_shared<DialogAsyncContext>();
context->env = env;
context->instanceId = Container::CurrentIdSafely();
napi_value result = CreateDialogPromise(env, context);
if (argc < MIN_PARAM_COUNT) {
RejectPromise(env, context->deferred, "The number of parameters must be greater than or equal to 1.",
ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[PARAM_INDEX_FIRST], &valueType);
if (valueType == napi_number) {
int32_t dialogId = 0;
napi_get_value_int32(env, argv[PARAM_INDEX_FIRST], &dialogId);
auto finishCallback = CreateDismissFinishCallback(context);
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
auto container = AceEngine::Get().GetContainer(context->instanceId);
CHECK_NULL_RETURN(container, result);
container->GetTaskExecutor()->PostTask(
[context]() {
if (context == nullptr) {
return;
}
napi_handle_scope scope = nullptr;
napi_open_handle_scope(context->env, &scope);
if (context->deferred) {
RejectPromise(context->env, context->deferred,
"Internal error.", ERROR_CODE_INTERNAL_ERROR);
context->deferred = nullptr;
}
napi_close_handle_scope(context->env, scope);
},
TaskExecutor::TaskType::JS, "ArkUIDialogDismissNoDelegate");
context = nullptr;
return result;
}
delegate->CloseCustomDialog(dialogId, std::move(finishCallback));
return result;
}
WeakPtr<NG::UINode> nodeWk;
if (!GetFrameNodePtr(env, argv[PARAM_INDEX_FIRST], nodeWk)) {
RejectPromise(env, context->deferred, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
context->deferred = nullptr;
return result;
}
auto finishCallback = CreateDismissFinishCallback(context);
auto delegate = EngineHelper::GetCurrentDelegateSafely();
if (!delegate) {
auto container = AceEngine::Get().GetContainer(context->instanceId);
CHECK_NULL_RETURN(container, result);
container->GetTaskExecutor()->PostTask(
[context]() {
if (context == nullptr) {
return;
}
napi_handle_scope scope = nullptr;
napi_open_handle_scope(context->env, &scope);
if (context->deferred) {
RejectPromise(context->env, context->deferred,
"Internal error.", ERROR_CODE_INTERNAL_ERROR);
context->deferred = nullptr;
}
napi_close_handle_scope(context->env, scope);
},
TaskExecutor::TaskType::JS, "ArkUIDialogDismissByNodeNoDelegate");
context = nullptr;
return result;
}
delegate->CloseCustomDialog(nodeWk, std::move(finishCallback));
return result;
}
napi_value DialogExport(napi_env env, napi_value exports)
{
napi_property_descriptor dialogDesc[] = {
DECLARE_NAPI_FUNCTION("presentDialog", JSPresentDialog),
DECLARE_NAPI_FUNCTION("presentCustomDialog", JSPresentCustomDialog),
DECLARE_NAPI_FUNCTION("updateCustomDialog", JSUpdateCustomDialog),
DECLARE_NAPI_FUNCTION("dismissDialog", JSDismissDialog),
};
NAPI_CALL(env, napi_define_properties(env, exports,
sizeof(dialogDesc) / sizeof(dialogDesc[0]), dialogDesc));
DialogControllerImpl::Define(env, exports);
return exports;
}
static napi_module_with_js dialogModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = DialogExport,
.nm_modname = "arkui.dialog",
.nm_priv = ((void*)0),
.nm_get_abc_code = NAPI_dialog_GetABCCode,
.nm_get_js_code = NAPI_dialog_GetJSCode,
};
extern "C" __attribute__((constructor)) void DialogRegister()
{
napi_module_with_js_register(&dialogModule);
}
}