1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components/rich_text/resource/rich_text_delegate.h"
17
18 #include <algorithm>
19 #include <iomanip>
20 #include <sstream>
21
22 #include "base/log/log.h"
23 #include "core/event/ace_event_helper.h"
24 #include "core/event/back_end_event_manager.h"
25 #include "frameworks/bridge/js_frontend/frontend_delegate_impl.h"
26
27 namespace OHOS::Ace {
28
29 namespace {
30
31 constexpr char RICH_TEXT_METHOD_CHANGE_VISIBILITY[] = "changeRichTextVisibility";
32 constexpr char RICH_TEXT_METHOD_HIDE_RICHTEXT_WHEN_POP[] = "hideRichTextWhenPop";
33 constexpr char RICH_TEXT_METHOD_HIDE_RICHTEXT_WHEN_PUSH[] = "hideRichTextWhenPush";
34 constexpr char RICH_TEXT_METHOD_SHOW_RICHTEXT[] = "showRichText";
35 constexpr char RICH_TEXT_METHOD_UPDATE_CONTENT[] = "updateRichTextContent";
36 constexpr char RICH_TEXT_METHOD_UPDATE_TRANSLATE[] = "updateTranslate";
37 constexpr char RICH_TEXT_METHOD_UPDATE_CONTENT_TRANSLATE[] = "updateContentTranslate";
38
39 constexpr char RICH_TEXT_EVENT_LOAD_START[] = "onPageStarted";
40 constexpr char RICH_TEXT_EVENT_LOAD_FINISHED[] = "onPageFinished";
41 constexpr char RICH_TEXT_EVENT_GOT_LAYOUT_PARAM[] = "onGotLayoutParam";
42 constexpr char RICH_TEXT_EVENT_LOAD_ERROR[] = "onPageError";
43
44 constexpr char RICH_TEXT_RESOURCE_NAME[] = "richtext";
45 constexpr char NTC_PARAM_RICH_TEXT[] = "richtext";
46
47 constexpr char NTC_PARAM_LEFT[] = "left";
48 constexpr char NTC_PARAM_TOP[] = "top";
49 constexpr char NTC_PARAM_URL[] = "url";
50 constexpr char NTC_PARAM_X[] = "x";
51 constexpr char NTC_PARAM_Y[] = "y";
52 constexpr char NTC_PARAM_LAYOUT_HEIGHT[] = "layoutHeight";
53 constexpr char NTC_PARAM_LAYOUT_WIDTH[] = "layoutWidth";
54 constexpr char NTC_PARAM_CONTENT_HEIGHT[] = "contentHeight";
55 constexpr char NTC_PARAM_RICHTEXT_VISIBILITY[] = "visibility";
56 constexpr char NTC_PARAM_PAGE_PATH[] = "pageRoutePath";
57 constexpr char NTC_PARAM_DESCRIPTION[] = "description";
58 constexpr char NTC_PARAM_CONTENT_DATA[] = "data";
59 constexpr char NTC_PARAM_ERROR_CODE[] = "errorCode";
60
61 constexpr char NTC_ERROR[] = "create error";
62 constexpr char RICH_TEXT_ERROR_CODE_CREATEFAIL[] = "error-rich-text-delegate-000001";
63 constexpr char RICH_TEXT_ERROR_MSG_CREATEFAIL[] = "create rich text delegate failed.";
64
65 } // namespace
66
~RichTextDelegate()67 RichTextDelegate::~RichTextDelegate()
68 {
69 ReleasePlatformResource();
70 }
71
ReleasePlatformResource()72 void RichTextDelegate::ReleasePlatformResource()
73 {
74 Stop();
75 Release();
76 }
77
Stop()78 void RichTextDelegate::Stop()
79 {
80 auto context = context_.Upgrade();
81 if (!context) {
82 LOGE("fail to get context when stop");
83 return;
84 }
85 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(),
86 TaskExecutor::TaskType::PLATFORM);
87 if (platformTaskExecutor.IsRunOnCurrentThread()) {
88 UnregisterEvent();
89 } else {
90 platformTaskExecutor.PostTask(
91 [weak = WeakClaim(this)] {
92 auto delegate = weak.Upgrade();
93 if (delegate) {
94 delegate->UnregisterEvent();
95 }
96 }, "ArkUIRichTextUnregisterEvent");
97 }
98 }
99
UnregisterEvent()100 void RichTextDelegate::UnregisterEvent()
101 {
102 auto context = context_.Upgrade();
103 if (!context) {
104 LOGE("fail to get context when unregister event");
105 return;
106 }
107 auto resRegister = context->GetPlatformResRegister();
108 resRegister->UnregisterEvent(MakeEventHash(RICH_TEXT_EVENT_LOAD_START));
109 resRegister->UnregisterEvent(MakeEventHash(RICH_TEXT_EVENT_LOAD_FINISHED));
110 resRegister->UnregisterEvent(MakeEventHash(RICH_TEXT_EVENT_LOAD_ERROR));
111 resRegister->UnregisterEvent(MakeEventHash(RICH_TEXT_EVENT_GOT_LAYOUT_PARAM));
112 }
113
CreatePlatformResource(const WeakPtr<PipelineContext> & context,const int32_t top,const int32_t left,const bool visible)114 void RichTextDelegate::CreatePlatformResource(
115 const WeakPtr<PipelineContext>& context, const int32_t top, const int32_t left, const bool visible)
116 {
117 context_ = context;
118 CreatePluginResource(context, top, left, visible);
119 InitWebEvent();
120 }
121
CreatePluginResource(const WeakPtr<PipelineContext> & context,const int32_t top,const int32_t left,const bool visible)122 void RichTextDelegate::CreatePluginResource(
123 const WeakPtr<PipelineContext>& context, const int32_t top, const int32_t left, const bool visible)
124 {
125 state_ = State::CREATING;
126
127 auto webCom = webComponent_.Upgrade();
128 if (!webCom) {
129 state_ = State::CREATEFAILED;
130 OnError(NTC_ERROR, "fail to call WebDelegate::Create due to webComponent is null");
131 return;
132 }
133
134 auto pipelineContext = context.Upgrade();
135 if (!pipelineContext) {
136 state_ = State::CREATEFAILED;
137 OnError(NTC_ERROR, "fail to call WebDelegate::Create due to context is null");
138 return;
139 }
140 context_ = context;
141 auto platformTaskExecutor = SingleTaskExecutor::Make(pipelineContext->GetTaskExecutor(),
142 TaskExecutor::TaskType::PLATFORM);
143 auto resRegister = pipelineContext->GetPlatformResRegister();
144 auto weakRes = AceType::WeakClaim(AceType::RawPtr(resRegister));
145 platformTaskExecutor.PostTask(
146 [weak = WeakClaim(this), weakRes, top, left, visible] {
147 auto delegate = weak.Upgrade();
148 if (!delegate) {
149 return;
150 }
151 auto webCom = delegate->webComponent_.Upgrade();
152 if (!webCom) {
153 delegate->OnError(NTC_ERROR, "fail to call WebDelegate::SetSrc PostTask");
154 }
155 auto resRegister = weakRes.Upgrade();
156 if (!resRegister) {
157 delegate->OnError(RICH_TEXT_ERROR_CODE_CREATEFAIL, RICH_TEXT_ERROR_MSG_CREATEFAIL);
158 return;
159 }
160
161 auto context = delegate->context_.Upgrade();
162 if (!context) {
163 LOGE("context is null");
164 return;
165 }
166
167 delegate->id_ = CREATING_ID;
168
169 std::string pageUrl;
170 int32_t pageId;
171 OHOS::Ace::Framework::DelegateClient::GetInstance().GetWebPageUrl(pageUrl, pageId);
172 std::string richTextVisible = visible ? "true" : "false";
173 delegate->pageUrl_ = pageUrl;
174 delegate->pageId_ = pageId;
175
176 std::stringstream paramStream;
177 paramStream << NTC_PARAM_RICH_TEXT << RICHTEXT_PARAM_EQUALS << delegate->id_ << RICHTEXT_PARAM_AND
178 << NTC_PARAM_CONTENT_DATA << RICHTEXT_PARAM_EQUALS << webCom->GetData() << RICHTEXT_PARAM_AND
179 << NTC_PARAM_LEFT << RICHTEXT_PARAM_EQUALS << left << RICHTEXT_PARAM_AND
180 << NTC_PARAM_TOP << RICHTEXT_PARAM_EQUALS << top << RICHTEXT_PARAM_AND
181 << NTC_PARAM_RICHTEXT_VISIBILITY << RICHTEXT_PARAM_EQUALS << richTextVisible
182 << RICHTEXT_PARAM_AND << NTC_PARAM_PAGE_PATH << RICHTEXT_PARAM_EQUALS << pageUrl;
183
184 std::string param = paramStream.str();
185 delegate->id_ = resRegister->CreateResource(RICH_TEXT_RESOURCE_NAME, param);
186 if (delegate->id_ == INVALID_ID) {
187 delegate->OnError(RICH_TEXT_ERROR_CODE_CREATEFAIL, RICH_TEXT_ERROR_MSG_CREATEFAIL);
188 return;
189 }
190 delegate->state_ = State::CREATED;
191 delegate->hash_ = delegate->MakeResourceHash();
192 delegate->RegisterWebEvent();
193 delegate->BindPopPageSuccessMethod();
194 delegate->BindIsPagePathInvalidMethod();
195 }, "ArkUIRichTextCreatePluginResource");
196 }
197
InitWebEvent()198 void RichTextDelegate::InitWebEvent()
199 {
200 auto webCom = webComponent_.Upgrade();
201 if (!webCom) {
202 state_ = State::CREATEFAILED;
203 OnError(NTC_ERROR, "fail to call get event due to rich text component is null");
204 return;
205 }
206 if (!webCom->GetPageStartedEventId().IsEmpty()) {
207 onPageStarted_ =
208 AceAsyncEvent<void(const std::string&)>::Create(webCom->GetPageStartedEventId(), context_);
209 }
210 if (!webCom->GetPageFinishedEventId().IsEmpty()) {
211 onPageFinished_ =
212 AceAsyncEvent<void(const std::string&)>::Create(webCom->GetPageFinishedEventId(), context_);
213 }
214 if (!webCom->GetPageErrorEventId().IsEmpty()) {
215 onPageError_ =
216 AceAsyncEvent<void(const std::string&)>::Create(webCom->GetPageErrorEventId(), context_);
217 }
218 }
219
RegisterWebEvent()220 void RichTextDelegate::RegisterWebEvent()
221 {
222 auto context = context_.Upgrade();
223 if (!context) {
224 return;
225 }
226 auto resRegister = context->GetPlatformResRegister();
227 resRegister->RegisterEvent(MakeEventHash(RICH_TEXT_EVENT_LOAD_START),
228 [weak = WeakClaim(this)](const std::string& param) {
229 auto delegate = weak.Upgrade();
230 if (delegate) {
231 delegate->OnPageStarted(param);
232 }
233 });
234 resRegister->RegisterEvent(MakeEventHash(RICH_TEXT_EVENT_LOAD_FINISHED),
235 [weak = WeakClaim(this)](const std::string& param) {
236 auto delegate = weak.Upgrade();
237 if (delegate) {
238 delegate->OnPageFinished(param);
239 }
240 });
241 resRegister->RegisterEvent(MakeEventHash(RICH_TEXT_EVENT_GOT_LAYOUT_PARAM),
242 [weak = WeakClaim(this)](const std::string& param) {
243 auto delegate = weak.Upgrade();
244 if (delegate) {
245 delegate->OnGotLayoutParam(param);
246 }
247 });
248 resRegister->RegisterEvent(MakeEventHash(RICH_TEXT_EVENT_LOAD_ERROR),
249 [weak = WeakClaim(this)](const std::string& param) {
250 auto delegate = weak.Upgrade();
251 if (delegate) {
252 delegate->OnPageError(param);
253 }
254 });
255 }
256
257 // upper ui component which inherits from WebComponent
258 // could implement some curtain createdCallback to customized controller interface
259 // eg: web.loadurl.
AddCreatedCallback(const CreatedCallback & createdCallback)260 void RichTextDelegate::AddCreatedCallback(const CreatedCallback& createdCallback)
261 {
262 if (!createdCallback || state_ == State::RELEASED) {
263 return;
264 }
265 createdCallbacks_.emplace_back(createdCallback);
266 }
267
AddWebLayoutChangeCallback(const UpdateWebViewLayoutCallback & layoutChangeCallback)268 void RichTextDelegate::AddWebLayoutChangeCallback(const UpdateWebViewLayoutCallback& layoutChangeCallback)
269 {
270 if (!layoutChangeCallback || state_ == State::RELEASED) {
271 return;
272 }
273 webviewLayoutCallback_ = layoutChangeCallback;
274 }
275
RemoveCreatedCallback()276 void RichTextDelegate::RemoveCreatedCallback()
277 {
278 if (state_ == State::RELEASED) {
279 return;
280 }
281 createdCallbacks_.pop_back();
282 }
283
AddReleasedCallback(const ReleasedCallback & releasedCallback)284 void RichTextDelegate::AddReleasedCallback(const ReleasedCallback& releasedCallback)
285 {
286 if (!releasedCallback || state_ == State::RELEASED) {
287 return;
288 }
289 releasedCallbacks_.emplace_back(releasedCallback);
290 }
291
RemoveReleasedCallback()292 void RichTextDelegate::RemoveReleasedCallback()
293 {
294 if (state_ == State::RELEASED) {
295 return;
296 }
297 releasedCallbacks_.pop_back();
298 }
299
UpdateRichTextData(const std::string & data)300 void RichTextDelegate::UpdateRichTextData(const std::string& data)
301 {
302 if (id_ == INVALID_ID) {
303 return;
304 }
305
306 hash_ = MakeResourceHash();
307 Method updateContentMethod = MakeMethodHash(RICH_TEXT_METHOD_UPDATE_CONTENT);
308 std::stringstream paramStream;
309 paramStream << NTC_PARAM_CONTENT_DATA << RICHTEXT_PARAM_EQUALS << data;
310 std::string param = paramStream.str();
311 CallResRegisterMethod(updateContentMethod, param, nullptr);
312 }
313
UpdateWebPostion(const int32_t top,const int32_t left)314 void RichTextDelegate::UpdateWebPostion(const int32_t top, const int32_t left)
315 {
316 hash_ = MakeResourceHash();
317
318 Method updateLayoutPositionMethod = MakeMethodHash(RICH_TEXT_METHOD_UPDATE_TRANSLATE);
319 std::stringstream paramStream;
320 paramStream << NTC_PARAM_LEFT << RICHTEXT_PARAM_EQUALS << left << RICHTEXT_PARAM_AND
321 << NTC_PARAM_TOP << RICHTEXT_PARAM_EQUALS << top;
322 std::string param = paramStream.str();
323 CallResRegisterMethod(updateLayoutPositionMethod, param, nullptr);
324 }
325
UpdateContentScroll(const int32_t x,const int32_t y)326 void RichTextDelegate::UpdateContentScroll(const int32_t x, const int32_t y)
327 {
328 hash_ = MakeResourceHash();
329
330 Method updateLayoutPositionMethod = MakeMethodHash(RICH_TEXT_METHOD_UPDATE_CONTENT_TRANSLATE);
331 std::stringstream paramStream;
332 paramStream << NTC_PARAM_X << RICHTEXT_PARAM_EQUALS << x << RICHTEXT_PARAM_AND
333 << NTC_PARAM_Y << RICHTEXT_PARAM_EQUALS << y;
334 std::string param = paramStream.str();
335 CallResRegisterMethod(updateLayoutPositionMethod, param, nullptr);
336 }
337
CallPopPageSuccessPageUrl(const std::string & url,const int32_t pageId)338 void RichTextDelegate::CallPopPageSuccessPageUrl(const std::string& url, const int32_t pageId)
339 {
340 if (url == pageUrl_ && pageId == pageId_) {
341 hash_ = MakeResourceHash();
342 Method reShowRichTextMethod = MakeMethodHash(RICH_TEXT_METHOD_SHOW_RICHTEXT);
343 std::stringstream paramStream;
344 paramStream << NTC_PARAM_PAGE_PATH << RICHTEXT_PARAM_EQUALS << url;
345 std::string param = paramStream.str();
346 CallResRegisterMethod(reShowRichTextMethod, param, nullptr);
347 }
348 }
349
CallIsPagePathInvalid(const bool & isPageInvalid)350 void RichTextDelegate::CallIsPagePathInvalid(const bool& isPageInvalid)
351 {
352 hash_ = MakeResourceHash();
353 Method hideRichTextMethod = MakeMethodHash(RICH_TEXT_METHOD_HIDE_RICHTEXT_WHEN_PUSH);
354 std::stringstream paramStream;
355 paramStream << RICHTEXT_PARAM_NONE;
356 std::string param = paramStream.str();
357 CallResRegisterMethod(hideRichTextMethod, param, nullptr);
358 }
359
HideRichText()360 void RichTextDelegate::HideRichText()
361 {
362 std::string pageUrl;
363 int32_t pageId;
364 OHOS::Ace::Framework::DelegateClient::GetInstance().GetWebPageUrl(pageUrl, pageId);
365
366 if (pageUrl != pageUrl_ || pageId != pageId_) {
367 hash_ = MakeResourceHash();
368 Method hideRichTextMethod = MakeMethodHash(RICH_TEXT_METHOD_HIDE_RICHTEXT_WHEN_POP);
369 std::stringstream paramStream;
370 paramStream << RICHTEXT_PARAM_NONE;
371 std::string param = paramStream.str();
372 CallResRegisterMethod(hideRichTextMethod, param, nullptr);
373 }
374 }
375
ChangeRichTextVisibility(const bool visible)376 void RichTextDelegate::ChangeRichTextVisibility(const bool visible)
377 {
378 hash_ = MakeResourceHash();
379 Method hideRichTextMethod = MakeMethodHash(RICH_TEXT_METHOD_CHANGE_VISIBILITY);
380 std::string richTextVisible = visible ? "true" : "false";
381 std::stringstream paramStream;
382 paramStream << NTC_PARAM_RICHTEXT_VISIBILITY << RICHTEXT_PARAM_EQUALS << richTextVisible;
383 std::string param = paramStream.str();
384 CallResRegisterMethod(hideRichTextMethod, param, nullptr);
385 }
OnPageStarted(const std::string & param)386 void RichTextDelegate::OnPageStarted(const std::string& param)
387 {
388 if (onPageStarted_) {
389 std::string param = std::string(R"("start", null, null)");
390 onPageStarted_(param);
391 }
392 }
393
OnPageFinished(const std::string & param)394 void RichTextDelegate::OnPageFinished(const std::string& param)
395 {
396 if (onPageFinished_) {
397 std::string param = std::string(R"("complete", null, null)");
398 onPageFinished_(param);
399 }
400 }
401
OnGotLayoutParam(const std::string & param)402 void RichTextDelegate::OnGotLayoutParam(const std::string& param)
403 {
404 int32_t layoutHeight = GetIntParam(param, NTC_PARAM_LAYOUT_HEIGHT);
405 int32_t contentHeight = GetIntParam(param, NTC_PARAM_CONTENT_HEIGHT);
406 int32_t layoutWidth = GetIntParam(param, NTC_PARAM_LAYOUT_WIDTH);
407 if (webviewLayoutCallback_) {
408 webviewLayoutCallback_(layoutWidth, layoutHeight, contentHeight);
409 }
410 }
411
OnPageError(const std::string & param)412 void RichTextDelegate::OnPageError(const std::string& param)
413 {
414 if (onPageError_) {
415 int32_t errorCode = GetIntParam(param, NTC_PARAM_ERROR_CODE);
416 std::string url = GetUrlStringParam(param, NTC_PARAM_URL);
417 std::string description = GetStringParam(param, NTC_PARAM_DESCRIPTION);
418
419 std::string paramUrl = std::string(R"(")").append(url)
420 .append(std::string(R"(")"))
421 .append(",");
422
423 std::string paramErrorCode = std::string(R"(")").append(NTC_PARAM_ERROR_CODE)
424 .append(std::string(R"(")"))
425 .append(":")
426 .append(std::to_string(errorCode))
427 .append(",");
428
429 std::string paramDesc = std::string(R"(")").append(NTC_PARAM_DESCRIPTION)
430 .append(std::string(R"(")"))
431 .append(":")
432 .append(std::string(R"(")")
433 .append(description)
434 .append(std::string(R"(")")));
435 std::string param = std::string(R"("error",{"url":)")
436 .append((paramUrl + paramErrorCode + paramDesc)
437 .append("},null"));
438 onPageError_(param);
439 }
440 }
441
GetUrlStringParam(const std::string & param,const std::string & name) const442 std::string RichTextDelegate::GetUrlStringParam(const std::string& param, const std::string& name) const
443 {
444 size_t len = name.length();
445 size_t posErrorCode = param.find(NTC_PARAM_ERROR_CODE);
446 size_t pos = param.find(name);
447 std::string result;
448
449 if (pos != std::string::npos && posErrorCode != std::string::npos) {
450 std::stringstream ss;
451
452 ss << param.substr(pos + 1 + len, posErrorCode - 5);
453 ss >> result;
454 }
455 return result;
456 }
457
BindPopPageSuccessMethod()458 void RichTextDelegate::BindPopPageSuccessMethod()
459 {
460 auto context = context_.Upgrade();
461 if (context) {
462 context->SetPopPageSuccessEventHandler(
463 [weak = WeakClaim(this)](const std::string& pageUrl, const int32_t pageId) {
464 std::string url = pageUrl.substr(0, pageUrl.length() - 3);
465 auto delegate = weak.Upgrade();
466 if (delegate) {
467 delegate->CallPopPageSuccessPageUrl(url, pageId);
468 }
469 });
470 }
471 }
472
BindIsPagePathInvalidMethod()473 void RichTextDelegate::BindIsPagePathInvalidMethod()
474 {
475 auto context = context_.Upgrade();
476 if (context) {
477 context->SetIsPagePathInvalidEventHandler([weak = WeakClaim(this)](bool& isPageInvalid) {
478 auto delegate = weak.Upgrade();
479 if (delegate) {
480 delegate->CallIsPagePathInvalid(isPageInvalid);
481 }
482 });
483 }
484 }
485
486 } // namespace OHOS::Ace
487