1 /*
2  * Copyright (c) 2021-2022 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 "frameworks/bridge/js_frontend/engine/jsi/jsi_component_api_bridge.h"
17 
18 #include "frameworks/bridge/common/dom/dom_list.h"
19 
20 namespace OHOS::Ace::Framework {
21 
JsGetScrollOffset(const shared_ptr<JsRuntime> & runtime,NodeId nodeId)22 shared_ptr<JsValue> JsiComponentApiBridge::JsGetScrollOffset(const shared_ptr<JsRuntime>& runtime, NodeId nodeId)
23 {
24     if (!runtime) {
25         LOGE("JsGetScrollOffset failed. runtime is null.");
26         return nullptr;
27     }
28     auto engine = static_cast<JsiEngineInstance*>(runtime->GetEmbedderData());
29     if (!engine) {
30         LOGE("JsGetScrollOffset failed. engine is null.");
31         return runtime->NewUndefined();
32     }
33     auto page = engine->GetRunningPage();
34     if (!page) {
35         LOGE("JsGetScrollOffset failed. page is null.");
36         return runtime->NewUndefined();
37     }
38     Offset offset;
39     auto task = [nodeId, page, &offset]() {
40         auto domDoc = page->GetDomDocument();
41         if (!domDoc) {
42             return;
43         }
44         auto domNode = domDoc->GetDOMNodeById(nodeId);
45         if (!domNode) {
46             return;
47         }
48         auto domList = AceType::DynamicCast<DOMList>(domNode);
49         if (domList) {
50             offset = domList->GetCurrentOffset();
51             return;
52         }
53 
54         auto scrollComponent = domNode->GetScrollComponent();
55         if (!scrollComponent) {
56             return;
57         }
58         auto controller = scrollComponent->GetScrollPositionController();
59         if (!controller) {
60             return;
61         }
62         offset = controller->GetCurrentOffset();
63     };
64     auto delegate = engine->GetFrontendDelegate();
65     if (!delegate) {
66         LOGE("JsGetScrollOffset failed. delegate is null.");
67         return runtime->NewUndefined();
68     }
69     delegate->PostSyncTaskToPage(task, "ArkUIJsGetScrollOffset");
70     shared_ptr<JsValue> offsetContext = runtime->NewObject();
71     offsetContext->SetProperty(runtime, "x", runtime->NewNumber(offset.GetX()));
72     offsetContext->SetProperty(runtime, "y", runtime->NewNumber(offset.GetY()));
73     return offsetContext;
74 }
75 
JsGetBoundingRect(const shared_ptr<JsRuntime> & runtime,NodeId nodeId)76 shared_ptr<JsValue> JsiComponentApiBridge::JsGetBoundingRect(const shared_ptr<JsRuntime>& runtime, NodeId nodeId)
77 {
78     if (!runtime) {
79         LOGE("JsGetBoundingRect failed. runtime is null.");
80         return nullptr;
81     }
82     auto engine = static_cast<JsiEngineInstance*>(runtime->GetEmbedderData());
83     if (!engine) {
84         LOGE("JsGetBoundingRect failed. engine is null.");
85         return runtime->NewUndefined();
86     }
87     auto delegate = engine->GetFrontendDelegate();
88     if (!delegate) {
89         LOGE("JsGetBoundingRect failed. delegate is null.");
90         return runtime->NewUndefined();
91     }
92     Rect boundingRect = delegate->GetBoundingRectData(nodeId);
93     shared_ptr<JsValue> rectContext = runtime->NewObject();
94     rectContext->SetProperty(runtime, "width", runtime->NewNumber(boundingRect.Width()));
95     rectContext->SetProperty(runtime, "height", runtime->NewNumber(boundingRect.Height()));
96     rectContext->SetProperty(runtime, "top", runtime->NewNumber(boundingRect.Top()));
97     rectContext->SetProperty(runtime, "left", runtime->NewNumber(boundingRect.Left()));
98     return rectContext;
99 }
100 
JsGetInspector(const shared_ptr<JsRuntime> & runtime,NodeId nodeId)101 shared_ptr<JsValue> JsiComponentApiBridge::JsGetInspector(const shared_ptr<JsRuntime>& runtime, NodeId nodeId)
102 {
103     if (!runtime) {
104         LOGE("JsGetInspector failed. runtime is null.");
105         return nullptr;
106     }
107     auto engine = static_cast<JsiEngineInstance*>(runtime->GetEmbedderData());
108     if (!engine) {
109         LOGE("JsGetInspector failed. engine is null.");
110         return runtime->NewUndefined();
111     }
112     auto delegate = engine->GetFrontendDelegate();
113     if (!delegate) {
114         LOGE("JsGetInspector failed. delegate is null.");
115         return runtime->NewUndefined();
116     }
117     auto attributes = delegate->GetInspector(nodeId);
118     shared_ptr<JsValue> result = runtime->NewString(attributes);
119     return result;
120 }
121 
JsScrollTo(const shared_ptr<JsRuntime> & runtime,const std::string & arguments,NodeId nodeId)122 void JsiComponentApiBridge::JsScrollTo(
123     const shared_ptr<JsRuntime>& runtime, const std::string& arguments, NodeId nodeId)
124 {
125     if (!runtime) {
126         LOGE("JsScrollTo failed. runtime is null.");
127         return;
128     }
129     auto engine = static_cast<JsiEngineInstance*>(runtime->GetEmbedderData());
130     if (!engine) {
131         LOGE("JsScrollTo failed. engine is null.");
132         return;
133     }
134     auto page = engine->GetRunningPage();
135     if (!page) {
136         LOGE("JsScrollTo failed. page is null.");
137         return;
138     }
139     auto task = [nodeId, page, arguments]() {
140         auto domDoc = page->GetDomDocument();
141         if (!domDoc) {
142             LOGE("JsScrollTo failed. dom document is null!");
143             return;
144         }
145         auto domNode = domDoc->GetDOMNodeById(nodeId);
146         if (!domNode) {
147             LOGE("JsScrollTo failed. dom node is null!");
148             return;
149         }
150         std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(arguments);
151         if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() < 1) {
152             LOGE("JsScrollTo failed. parse args error");
153             return;
154         }
155         std::unique_ptr<JsonValue> scrollToPara = argsValue->GetArrayItem(0);
156         int32_t index = scrollToPara->GetInt("index", 0);
157         auto domList = AceType::DynamicCast<DOMList>(domNode);
158         if (domList) {
159             // list has specialized scrollTo method.
160             domList->ScrollToMethod(index);
161             return;
162         }
163 
164         auto scrollComponent = domNode->GetScrollComponent();
165         if (!scrollComponent) {
166             return;
167         }
168         auto controller = scrollComponent->GetScrollPositionController();
169         if (!controller) {
170             return;
171         }
172 
173         std::string id = scrollToPara->GetString("id", "");
174         double position = scrollToPara->GetDouble("position", 0.0);
175         double duration = scrollToPara->GetDouble("duration", 300.0); // Default duration is 300ms.
176         std::string timingFunction = scrollToPara->GetString("timingFunction", "ease");
177         std::string successId = scrollToPara->GetString("success", "");
178         std::string failId = scrollToPara->GetString("fail", "");
179         std::string completeId = scrollToPara->GetString("complete", "");
180         auto context = domNode->GetPipelineContext();
181         auto callback = [context, successId, completeId]() {
182             auto refContext = context.Upgrade();
183             if (refContext) {
184                 refContext->SendCallbackMessageToFrontend(successId, std::string("\"success\",null"));
185                 refContext->SendCallbackMessageToFrontend(completeId, std::string("\"complete\",null"));
186             }
187         };
188 
189         bool result = false;
190         if (scrollToPara->Contains("position")) {
191             result = controller->AnimateTo(position, duration, CreateCurve(timingFunction), false, callback);
192         } else if (scrollToPara->Contains("id") && !id.empty()) {
193             result = controller->AnimateToTarget(id, duration, CreateCurve(timingFunction), false, callback);
194         } else {
195             LOGW("JsScrollTo failed. param not valid.");
196         }
197         if (!result) {
198             auto refContext = context.Upgrade();
199             if (refContext) {
200                 refContext->SendCallbackMessageToFrontend(failId, std::string("\"fail\",null"));
201                 refContext->SendCallbackMessageToFrontend(completeId, std::string("\"complete\",null"));
202             }
203         }
204     };
205 
206     auto delegate = engine->GetFrontendDelegate();
207     if (!delegate) {
208         LOGE("JsScrollTo failed. delegate is null.");
209         return;
210     }
211     delegate->PostSyncTaskToPage(task, "ArkUIJsScrollTo");
212 }
213 
214 } // namespace OHOS::Ace::Framework