• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "frameworks/bridge/js_frontend/engine/v8/v8_component_api_bridge.h"
17 
18 #include "core/animation/curves.h"
19 #include "frameworks/bridge/common/dom/dom_list.h"
20 #include "frameworks/bridge/common/utils/utils.h"
21 #include "frameworks/bridge/js_frontend/engine/v8/v8_utils.h"
22 
23 namespace OHOS::Ace::Framework {
24 
JsGetScrollOffset(v8::Isolate * isolate,NodeId nodeId)25 v8::Local<v8::Object> V8ComponentApiBridge::JsGetScrollOffset(v8::Isolate* isolate, NodeId nodeId)
26 {
27     if (!isolate) {
28         return v8::Local<v8::Object>();
29     }
30     Offset offset;
31     auto page = static_cast<RefPtr<JsAcePage>*>(isolate->GetData(V8EngineInstance::RUNNING_PAGE));
32     if (page == nullptr) {
33         return v8::Local<v8::Object>();
34     }
35     auto task = [nodeId, page, &offset]() {
36         auto domDoc = (*page)->GetDomDocument();
37         if (!domDoc) {
38             return;
39         }
40 
41         auto domNode = domDoc->GetDOMNodeById(nodeId);
42         if (!domNode) {
43             return;
44         }
45         auto domList = AceType::DynamicCast<DOMList>(domNode);
46         if (domList) {
47             offset = domList->GetCurrentOffset();
48             return;
49         }
50 
51         auto scrollComponent = domNode->GetScrollComponent();
52         if (!scrollComponent) {
53             return;
54         }
55         auto controller = scrollComponent->GetScrollPositionController();
56         if (!controller) {
57             return;
58         }
59         offset = controller->GetCurrentOffset();
60     };
61 
62     auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
63     if (delegate == nullptr) {
64         return v8::Local<v8::Object>();
65     }
66     (*delegate)->PostSyncTaskToPage(task);
67 
68     auto ctx = isolate->GetCurrentContext();
69     v8::Local<v8::Object> offsetContext = v8::Object::New(ctx->GetIsolate());
70     offsetContext
71         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "x").ToLocalChecked(),
72             v8::Number::New(ctx->GetIsolate(), offset.GetX()))
73         .ToChecked();
74     offsetContext
75         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "y").ToLocalChecked(),
76             v8::Number::New(ctx->GetIsolate(), offset.GetY()))
77         .ToChecked();
78     return offsetContext;
79 }
80 
JsGetBoundingRect(v8::Isolate * isolate,NodeId nodeId)81 v8::Local<v8::Object> V8ComponentApiBridge::JsGetBoundingRect(v8::Isolate* isolate, NodeId nodeId)
82 {
83     if (!isolate) {
84         return v8::Local<v8::Object>();
85     }
86     auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
87     if (delegate == nullptr) {
88         return v8::Local<v8::Object>();
89     }
90 
91     Rect boundingRect = (*delegate)->GetBoundingRectData(nodeId);
92     auto ctx = isolate->GetCurrentContext();
93     v8::Local<v8::Object> rectContext = v8::Object::New(ctx->GetIsolate());
94     rectContext
95         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "width").ToLocalChecked(),
96             v8::Number::New(ctx->GetIsolate(), boundingRect.Width()))
97         .ToChecked();
98     rectContext
99         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "height").ToLocalChecked(),
100             v8::Number::New(ctx->GetIsolate(), boundingRect.Height()))
101         .ToChecked();
102     rectContext
103         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "top").ToLocalChecked(),
104             v8::Number::New(ctx->GetIsolate(), boundingRect.Top()))
105         .ToChecked();
106     rectContext
107         ->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "left").ToLocalChecked(),
108             v8::Number::New(ctx->GetIsolate(), boundingRect.Left()))
109         .ToChecked();
110     return rectContext;
111 }
112 
JsScrollTo(const v8::FunctionCallbackInfo<v8::Value> & args,const std::string & arguments,NodeId nodeId)113 void V8ComponentApiBridge::JsScrollTo(
114     const v8::FunctionCallbackInfo<v8::Value>& args, const std::string& arguments, NodeId nodeId)
115 {
116     v8::Isolate* isolate = args.GetIsolate();
117     if (isolate == nullptr) {
118         LOGE("V8ComponentApiBridge::JsScrollTo isolate is null!");
119         return;
120     }
121     v8::HandleScope handleScope(isolate);
122     auto context = isolate->GetCurrentContext();
123     if (context.IsEmpty()) {
124         LOGE("V8ComponentApiBridge::JsScrollTo context is empty!");
125         return;
126     }
127     v8::Local<v8::External> data = v8::Local<v8::External>::Cast(args.Data());
128     V8EngineInstance* engineInstance = static_cast<V8EngineInstance*>(data->Value());
129     if (engineInstance == nullptr) {
130         LOGE("V8ComponentApiBridge::JsScrollTo engineInstance is null!");
131         return;
132     }
133 
134     auto page = static_cast<RefPtr<JsAcePage>*>(isolate->GetData(V8EngineInstance::RUNNING_PAGE));
135     if (page == nullptr) {
136         LOGE("V8ComponentApiBridge::JsScrollTo page is null");
137         return;
138     }
139 
140     auto task = [nodeId, page, arguments]() {
141         auto domDoc = (*page)->GetDomDocument();
142         if (!domDoc) {
143             LOGE("V8ComponentApiBridge::JsScrollTo dom document is null!");
144             return;
145         }
146 
147         auto domNode = domDoc->GetDOMNodeById(nodeId);
148         if (!domNode) {
149             LOGE("V8ComponentApiBridge::JsScrollTo node not exist!");
150             return;
151         }
152 
153         std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(arguments);
154         if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() < 1) {
155             LOGE("V8ComponentApiBridge::JsScrollTo parse args error");
156             return;
157         }
158         std::unique_ptr<JsonValue> scrollToPara = argsValue->GetArrayItem(0);
159         int32_t index = scrollToPara->GetInt("index", 0);
160         auto domList = AceType::DynamicCast<DOMList>(domNode);
161         if (domList) {
162             // list has specialized scrollTo method.
163             domList->ScrollToMethod(index);
164             return;
165         }
166 
167         auto scrollComponent = domNode->GetScrollComponent();
168         if (!scrollComponent) {
169             return;
170         }
171         auto controller = scrollComponent->GetScrollPositionController();
172         if (!controller) {
173             return;
174         }
175 
176         std::string id = scrollToPara->GetString("id", "");
177         double position = scrollToPara->GetDouble("position", 0.0);
178         double duration = scrollToPara->GetDouble("duration", 300.0); // Default duration is 300ms.
179         std::string timingFunction = scrollToPara->GetString("timingFunction", "ease");
180         std::string successId = scrollToPara->GetString("success", "");
181         std::string failId = scrollToPara->GetString("fail", "");
182         std::string completeId = scrollToPara->GetString("complete", "");
183         auto context = domNode->GetPipelineContext();
184         auto callback = [context, successId, completeId]() {
185             auto refContext = context.Upgrade();
186             if (refContext) {
187                 refContext->SendCallbackMessageToFrontend(successId, std::string("\"success\",null"));
188                 refContext->SendCallbackMessageToFrontend(completeId, std::string("\"complete\",null"));
189             }
190         };
191 
192         bool result = false;
193         if (scrollToPara->Contains("position")) {
194             result = controller->AnimateTo(position, duration, CreateCurve(timingFunction), false, callback);
195         } else if (scrollToPara->Contains("id") && !id.empty()) {
196             result = controller->AnimateToTarget(id, duration, CreateCurve(timingFunction), false, callback);
197         } else {
198             LOGW("V8ComponentApiBridge::JsScrollTo param not valid.");
199         }
200         if (!result) {
201             auto refContext = context.Upgrade();
202             if (refContext) {
203                 refContext->SendCallbackMessageToFrontend(failId, std::string("\"fail\",null"));
204                 refContext->SendCallbackMessageToFrontend(completeId, std::string("\"complete\",null"));
205             }
206         }
207     };
208 
209     auto delegate = engineInstance->GetDelegate();
210     if (!delegate) {
211         LOGE("V8ComponentApiBridge::JsScrollTo delegate is null");
212         return;
213     }
214     delegate->PostSyncTaskToPage(task);
215 }
216 
JsAddVisibleListener(const v8::FunctionCallbackInfo<v8::Value> & args,const std::string & arguments,NodeId nodeId)217 v8::Local<v8::Object> V8ComponentApiBridge::JsAddVisibleListener(
218     const v8::FunctionCallbackInfo<v8::Value>& args, const std::string& arguments, NodeId nodeId)
219 {
220     v8::Isolate* isolate = args.GetIsolate();
221     if (isolate == nullptr) {
222         LOGE("V8ComponentApiBridge::JsAddVisibleListener isolate is null!");
223         return v8::Local<v8::Object>();
224     }
225     v8::HandleScope handleScope(isolate);
226     auto ctx = isolate->GetCurrentContext();
227     if (ctx.IsEmpty()) {
228         LOGE("V8ComponentApiBridge::JsAddVisibleListener context is empty!");
229         return v8::Local<v8::Object>();
230     }
231     std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(arguments);
232     if (!argsPtr) {
233         LOGE("V8ComponentApiBridge::JsAddVisibleListener argsPtr is null!");
234         return v8::Local<v8::Object>();
235     }
236     auto observerParam = argsPtr->GetArrayItem(0);
237     if (!observerParam) {
238         return v8::Local<v8::Object>();
239     }
240     std::string type;
241     std::vector<double> ratios;
242     if (observerParam->GetValue("type") != nullptr && observerParam->GetValue("type")->IsString()) {
243         type = observerParam->GetValue("type")->GetString();
244     }
245     if (observerParam->GetValue("ratios") != nullptr && observerParam->GetValue("ratios")->IsArray()) {
246         auto ratioLen = observerParam->GetValue("ratios")->GetArraySize();
247         for (int32_t i = 0; i < ratioLen; ++i) {
248             auto ratioVal = observerParam->GetValue("ratios")->GetArrayItem(i);
249             if (ratioVal->IsNumber()) {
250                 ratios.emplace_back(ratioVal->GetDouble());
251             }
252         }
253     }
254     auto listener = v8::Object::New(ctx->GetIsolate());
255     listener->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__nodeId").ToLocalChecked(),
256         v8::Int32::New(ctx->GetIsolate(), nodeId)).ToChecked();
257     listener->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__type").ToLocalChecked(),
258         v8::String::NewFromUtf8(ctx->GetIsolate(), type.c_str()).ToLocalChecked()).ToChecked();
259     for (int32_t j = 0; j < static_cast<int32_t>(ratios.size()); ++j) {
260         std::string ratioName = "__ratio" + std::to_string(j);
261         listener->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), ratioName.c_str()).ToLocalChecked(),
262             v8::Number::New(ctx->GetIsolate(), ratios[j])).ToChecked();
263     }
264     listener->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "__ratioLen").ToLocalChecked(),
265                   v8::Number::New(ctx->GetIsolate(), static_cast<int32_t>(ratios.size()))).ToChecked();
266     listener->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "observe").ToLocalChecked(),
267         v8::Function::New(ctx, JsVisibleListenerOn, v8::Local<v8::Value>(), 1).ToLocalChecked()).ToChecked();
268     listener->Set(ctx, v8::String::NewFromUtf8(ctx->GetIsolate(), "unobserve").ToLocalChecked(),
269         v8::Function::New(ctx, JsVisibleListenerOff, v8::Local<v8::Value>(), 0).ToLocalChecked()).ToChecked();
270     return listener;
271 }
272 
JsVisibleListenerOn(const v8::FunctionCallbackInfo<v8::Value> & args)273 void V8ComponentApiBridge::JsVisibleListenerOn(const v8::FunctionCallbackInfo<v8::Value>& args)
274 {
275     v8::Isolate* isolate = args.GetIsolate();
276     v8::HandleScope handleScope(isolate);
277     auto context = isolate->GetCurrentContext();
278     auto observerValue = args.Holder();
279     NodeId id = observerValue->Get(context, v8::String::NewFromUtf8(isolate, "__nodeId").ToLocalChecked())
280         .ToLocalChecked()->Int32Value(context).ToChecked();
281     auto ratioLength = observerValue->Get(context, v8::String::NewFromUtf8(isolate, "__ratioLen").ToLocalChecked())
282         .ToLocalChecked()->NumberValue(context).ToChecked();
283     std::vector<double> ratios;
284     for (int32_t i = 0; i < ratioLength; ++i) {
285         auto ratioName = "__ratio" + std::to_string(i);
286         auto ratio = observerValue->Get(context, v8::String::NewFromUtf8(isolate, ratioName.c_str()).ToLocalChecked())
287             .ToLocalChecked()->NumberValue(context).ToChecked();
288         if (LessOrEqual(ratio, 0.0)) {
289             ratio = 0.001;
290         }
291         if (GreatOrEqual(ratio, 1.0)) {
292             ratio = 0.999;
293         }
294         ratios.emplace_back(ratio);
295     }
296 
297     if (!args[0]->IsFunction()) {
298         LOGW("args is not function");
299         return;
300     }
301     v8::Local<v8::Function> jsFunc = v8::Local<v8::Function>::Cast(args[0]);
302     auto callbackObject = AceType::MakeRefPtr<VisibleListenerCallback>(context, isolate, jsFunc, id);
303     auto stagingPage = static_cast<RefPtr<JsAcePage>*>(isolate->GetData(V8EngineInstance::STAGING_PAGE));
304     int32_t instanceId = (*stagingPage)->GetPageId();
305     auto jsCallback = [callbackObj = callbackObject, isolate, instanceId](bool visible, double ratio) {
306         v8::HandleScope handleScope(isolate);
307         auto context = isolate->GetCurrentContext();
308         v8::Isolate::Scope isolateScope(isolate);
309         v8::Context::Scope contextScope(context);
310         v8::TryCatch tryCatch(isolate);
311         v8::Local<v8::Function> jsFunc = callbackObj->GetJsObject();
312 
313         v8::Local<v8::Boolean> visibleJsVal = v8::Boolean::New(context->GetIsolate(), visible);
314         v8::Local<v8::Number> ratioJsVal = v8::Number::New(context->GetIsolate(), ratio);
315         v8::Local<v8::Value> argv[] = { visibleJsVal, ratioJsVal };
316         int32_t len = sizeof(argv) / sizeof(argv[0]);
317         v8::Local<v8::Value> res;
318         bool succ = jsFunc->Call(context, context->Global(), len, argv).ToLocal(&res);
319         if (!succ) {
320             LOGE("call jsFunc failed!");
321             V8Utils::JsStdDumpErrorAce(isolate, &tryCatch, JsErrorType::COMPILE_ERROR, instanceId);
322         }
323     };
324 
325     auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
326     for (int32_t j = 0; j < static_cast<int32_t>(ratios.size()); ++j) {
327         (*delegate)->PushJsCallbackToRenderNode(id, ratios[j], jsCallback);
328     }
329 }
330 
JsVisibleListenerOff(const v8::FunctionCallbackInfo<v8::Value> & args)331 void V8ComponentApiBridge::JsVisibleListenerOff(const v8::FunctionCallbackInfo<v8::Value>& args)
332 {
333     v8::Isolate* isolate = args.GetIsolate();
334     v8::HandleScope handleScope(isolate);
335     auto context = isolate->GetCurrentContext();
336     auto observerValue = args.Holder();
337     NodeId id = observerValue->Get(context, v8::String::NewFromUtf8(isolate, "__nodeId").ToLocalChecked())
338         .ToLocalChecked()->Int32Value(context).ToChecked();
339     auto delegate = static_cast<RefPtr<FrontendDelegate>*>(isolate->GetData(V8EngineInstance::FRONTEND_DELEGATE));
340     (*delegate)->RemoveVisibleChangeNode(id);
341 }
342 
343 } // namespace OHOS::Ace::Framework