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 #ifndef FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_ENGINE_QUICKJS_QJS_DECLARATIVE_ENGINE_H 17 #define FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_ENGINE_QUICKJS_QJS_DECLARATIVE_ENGINE_H 18 19 #include <unordered_map> 20 21 #include "third_party/quickjs/quickjs.h" 22 23 #if defined(PREVIEW) 24 #include "adapter/preview/osal/request_data.h" 25 #endif 26 #include "frameworks/bridge/js_frontend/engine/common/js_engine.h" 27 #include "frameworks/bridge/js_frontend/js_ace_page.h" 28 29 namespace OHOS::Ace::Framework { 30 31 class QJSDeclarativeEngineInstance final : public AceType, public JsEngineInstance { 32 public: 33 explicit QJSDeclarativeEngineInstance(const RefPtr<FrontendDelegate>& delegate, int32_t instanceId = 0) context_(nullptr)34 : context_(nullptr), frontendDelegate_(delegate), instanceId_(instanceId), dispatcher_(nullptr) 35 {} 36 37 ~QJSDeclarativeEngineInstance() override; 38 void FlushCommandBuffer(void* context, const std::string& command) override; 39 bool InitJSEnv( 40 JSRuntime* runtime, JSContext* context, const std::unordered_map<std::string, void*>& extraNativeObject); 41 42 bool InitAceModules(const char* start, size_t length, const char* fileName); 43 44 void InitJsNativeModuleObject(JSContext* ctx); 45 void InitJsExportsUtilObject(JSContext* ctx); 46 47 void loadDocument(); 48 49 void FreeGroupJsBridge(); 50 GetQJSRuntime()51 static JSRuntime* GetQJSRuntime() 52 { 53 return runtime_; 54 } 55 GetQJSContext()56 JSContext* GetQJSContext() const 57 { 58 return context_; 59 } 60 61 static QJSDeclarativeEngineInstance* UnWrapEngineInstance(JSContext* ctx); 62 GetRunningPage()63 RefPtr<JsAcePage> GetRunningPage() const 64 { 65 std::lock_guard<std::mutex> lock(mutex_); 66 return runningPage_; 67 } 68 69 static RefPtr<JsAcePage> GetRunningPage(JSContext* ctx); 70 static RefPtr<JsAcePage> GetStagingPage(JSContext* ctx); 71 static JSContext* GetCurrentContext(); 72 73 void SetStagingPage(const RefPtr<JsAcePage>& page); 74 GetStagingPage()75 RefPtr<JsAcePage> GetStagingPage() const 76 { 77 std::lock_guard<std::mutex> lock(mutex_); 78 return stagingPage_; 79 } 80 void ResetStagingPage(const RefPtr<JsAcePage>& page); 81 82 void SetRunningPage(const RefPtr<JsAcePage>& page); 83 84 static void PostJsTask(JSContext* ctx, std::function<void()>&& task); 85 static void TriggerPageUpdate(JSContext* ctx); 86 static RefPtr<PipelineBase> GetPipelineContext(JSContext* ctx); 87 SetJsMessageDispatcher(const RefPtr<JsMessageDispatcher> & dispatcher)88 void SetJsMessageDispatcher(const RefPtr<JsMessageDispatcher>& dispatcher) 89 { 90 dispatcher_ = dispatcher; 91 } 92 GetDelegate()93 RefPtr<FrontendDelegate> GetDelegate() const 94 { 95 return frontendDelegate_; 96 } 97 98 JSValue CompileSource(std::string instanceName, std::string url, const char* buf, size_t bufSize); 99 CallPlatformFunction(const std::string & channel,std::vector<uint8_t> && data,int32_t id,int32_t groupType)100 void CallPlatformFunction(const std::string& channel, std::vector<uint8_t>&& data, int32_t id, int32_t groupType) 101 { 102 auto dispatcher = dispatcher_.Upgrade(); 103 if (dispatcher) { 104 dispatcher->Dispatch(channel, std::move(data), id, groupType); 105 } 106 } 107 108 void CallAnimationFinishJs(JSValue animationContext); 109 void CallAnimationCancelJs(JSValue animationContext); 110 111 #if defined(PREVIEW) CallCurlFunction(const OHOS::Ace::RequestData & requestData,int32_t callbackId)112 bool CallCurlFunction(const OHOS::Ace::RequestData& requestData, int32_t callbackId) 113 { 114 auto dispatcher = dispatcher_.Upgrade(); 115 if (dispatcher) { 116 dispatcher->CallCurlFunction(requestData, callbackId); 117 return true; 118 } else { 119 LOGW("Dispatcher Upgrade fail when dispatch request message to platform"); 120 return false; 121 } 122 } 123 #endif 124 125 bool ExecuteDocumentJS(JSValue jsCode); 126 127 void FireAsyncEvent(const std::string& eventId, const std::string& param); 128 129 /** Pushes the given command on the currently loading page 130 * if conditions apply also flushed the command queue 131 * if forceFlush=true is specified the command buffer will be flushed. 132 * even if conditions are not met. 133 */ 134 void PushJSCommand(const RefPtr<JsCommand>& jsCommand, bool forceFlush = false) const; 135 static void PushJSCommand(JSContext* ctx, const RefPtr<JsCommand>& jsCommand, bool forceFlush = false); 136 PluginErrorCallback(int32_t callbackId,int32_t errorCode,std::string && errorMessage)137 void PluginErrorCallback(int32_t callbackId, int32_t errorCode, std::string&& errorMessage) 138 { 139 auto dispatcher = dispatcher_.Upgrade(); 140 if (dispatcher) { 141 dispatcher->DispatchPluginError(callbackId, errorCode, std::move(errorMessage)); 142 } 143 } 144 ChangeLocale(const std::string & language,const std::string & countryOrRegion)145 void ChangeLocale(const std::string& language, const std::string& countryOrRegion) 146 { 147 if (frontendDelegate_) { 148 frontendDelegate_->ChangeLocale(language, countryOrRegion); 149 } 150 } 151 152 void RunGarbageCollection(); 153 154 static std::unique_ptr<JsonValue> GetI18nStringResource( 155 const std::string& targetStringKey, const std::string& targetStringValue); 156 static std::string GetMediaResource(const std::string& targetMediaFileName); 157 static int EvalBuf(JSContext* ctx, const char* buf, size_t bufLen, const char* filename, int evalFlags); 158 159 #if defined(PREVIEW) SetPreviewFlag(bool flag)160 void SetPreviewFlag(bool flag) 161 { 162 isComponentPreview_ = flag; 163 } 164 GetPreviewFlag()165 bool GetPreviewFlag() const 166 { 167 return isComponentPreview_; 168 } 169 GetRequiredComponent()170 std::string GetRequiredComponent() const 171 { 172 return requiredComponent_; 173 } 174 SetRequiredComponent(const std::string & componentName)175 void SetRequiredComponent(const std::string& componentName) 176 { 177 requiredComponent_ = componentName; 178 } 179 AddPreviewComponent(const std::string & componentName,JSValue & componentObj)180 void AddPreviewComponent(const std::string& componentName, JSValue& componentObj) 181 { 182 previewComponents_.insert_or_assign(componentName, componentObj); 183 } 184 GetPreviewComponent(const std::string & componentName)185 JSValue GetPreviewComponent(const std::string& componentName) 186 { 187 auto iter = previewComponents_.find(componentName); 188 if (iter != previewComponents_.end()) { 189 return iter->second; 190 } 191 return JS_UNDEFINED; 192 } 193 #endif 194 195 private: 196 #if defined(PREVIEW) 197 bool isComponentPreview_ = false; 198 std::string requiredComponent_ {}; 199 std::unordered_map<std::string, JSValue> previewComponents_; 200 #endif 201 202 void output_object_code(JSContext* ctx, int fho, JSValueConst obj); 203 JSValue eval_binary_buf(JSContext* ctx, const uint8_t* buf, size_t buf_len); 204 205 thread_local static JSRuntime* runtime_; 206 JSContext* context_ = nullptr; 207 RefPtr<FrontendDelegate> frontendDelegate_; 208 int32_t instanceId_; 209 static std::map<std::string, std::string> mediaResourceFileMap_; 210 static std::unique_ptr<JsonValue> currentConfigResourceData_; 211 212 // runningPage_ is the page that is loaded and rendered successfully, while stagingPage_ is to 213 // handle all page routing situation, which include two stages: 214 // - Loading stage: when a new page is loaded by qjs engine but not rendered, stagingPage_ point to 215 // a new created page, which is different with runningPage_, the DOM build operations should call 216 // this one, such as domCreateBody, domAddElement. 217 // - Running stage: If the stagingPage_ rendered successfully, the runningPage_ will update to stagingPage_. 218 // If the stagingPage_ render failed, it will reset to runningPage_. So in running stage, runningPage_ 219 // and stagingPage_ point to the same page. But it's better to use runningPage_ in dom update tasks, 220 // such as removeElement, updateElementAttrs and updateElementStyles. 221 RefPtr<JsAcePage> runningPage_; 222 RefPtr<JsAcePage> stagingPage_; 223 224 WeakPtr<JsMessageDispatcher> dispatcher_; 225 mutable std::mutex mutex_; 226 227 ACE_DISALLOW_COPY_AND_MOVE(QJSDeclarativeEngineInstance); 228 }; 229 230 } // namespace OHOS::Ace::Framework 231 232 #endif // FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_ENGINE_QUICKJS_QJS_DECLARATIVE_ENGINE_H 233