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/pipeline/base/composed_element.h"
17
18 #include "base/log/dump_log.h"
19 #include "base/log/log.h"
20 #include "base/utils/utils.h"
21 #include "core/common/container.h"
22 #include "core/common/frontend.h"
23 #include "core/components/flex/flex_item_element.h"
24 #include "core/components/page/page_element.h"
25 #include "core/event/ace_event_helper.h"
26 #include "core/pipeline/base/composed_component.h"
27 #include "core/pipeline/base/render_element.h"
28
29 namespace OHOS::Ace {
30
ComposedElement(const ComposeId & id)31 ComposedElement::ComposedElement(const ComposeId& id) : id_(id)
32 {
33 type_ = COMPOSED_ELEMENT;
34 }
35
Detached()36 void ComposedElement::Detached()
37 {
38 auto context = context_.Upgrade();
39 if (addedToMap_ && context) {
40 context->RemoveComposedElement(id_, AceType::Claim(this));
41 addedToMap_ = false;
42 }
43 }
44
Deactivate()45 void ComposedElement::Deactivate()
46 {
47 Detached();
48 UmountRender();
49 }
50
UmountRender()51 void ComposedElement::UmountRender()
52 {
53 for (const auto& child : children_) {
54 child->UmountRender();
55 }
56 }
57
PerformBuild()58 void ComposedElement::PerformBuild()
59 {
60 auto context = context_.Upgrade();
61 if (!context) {
62 LOGW("Invalid pipeline stop building");
63 return;
64 }
65
66 if (!addedToMap_) {
67 context->AddComposedElement(id_, AceType::Claim(this));
68 addedToMap_ = true;
69 }
70
71 auto component = HasRenderFunction() ? CallRenderFunction(component_) : BuildChild();
72
73 if (!component && Container::IsCurrentUsePartialUpdate()) {
74 // partial update re-render code path calls JSView::MakeElementUpdatesToCompleteRerender
75 // this function returns no component, ComposedElement does not require update
76 return;
77 }
78
79 auto child = children_.empty() ? nullptr : children_.front();
80 auto composedComponent = AceType::DynamicCast<ComposedComponent>(component_);
81 if (composedComponent) {
82 auto composedChild = composedComponent->GetChild();
83 if (HasRenderFunction() && composedComponent->GetNeedReserveChild()) {
84 auto flexItem = AceType::DynamicCast<SoleChildComponent>(composedChild);
85 if (flexItem) {
86 flexItem->SetChild(component);
87 UpdateChild(child, flexItem);
88 return;
89 }
90 }
91 }
92
93 UpdateChild(child, component);
94 }
95
Update()96 void ComposedElement::Update()
97 {
98 const RefPtr<ComposedComponent> compose = AceType::DynamicCast<ComposedComponent>(component_);
99 if (compose != nullptr) {
100 LOGD("Update on %{public}s with %{public}s, elmtId %{public}d", AceType::TypeName(this),
101 AceType::TypeName(compose), compose->GetElementId());
102 name_ = compose->GetName();
103 SetElementId(compose->GetElementId());
104 if (id_ != compose->GetId()) {
105 auto context = context_.Upgrade();
106 if (addedToMap_ && context != nullptr) {
107 context->RemoveComposedElement(id_, AceType::Claim(this));
108 context->AddComposedElement(compose->GetId(), AceType::Claim(this));
109 }
110 id_ = compose->GetId();
111 }
112 compose->ClearNeedUpdate();
113 }
114 if (HasPageTransitionFunction()) {
115 auto pageElement = GetPageElement();
116 if (pageElement) {
117 pageElement->SetPageTransitionFunction(std::move(pageTransitionFunction_));
118 }
119 }
120 }
121
BuildChild()122 RefPtr<Component> ComposedElement::BuildChild()
123 {
124 RefPtr<ComposedComponent> compose = AceType::DynamicCast<ComposedComponent>(component_);
125 if (compose != nullptr) {
126 return compose->GetChild();
127 }
128 return nullptr;
129 }
130
Apply(const RefPtr<Element> & child)131 void ComposedElement::Apply(const RefPtr<Element>& child)
132 {
133 if (!child) {
134 LOGE("Element child is null");
135 return;
136 }
137
138 if (!applyFunction_) {
139 LOGE("No apply function");
140 return;
141 }
142
143 if (child->GetType() == RENDER_ELEMENT) {
144 // Directly attach the RenderNode if child is RenderElement.
145 applyFunction_(AceType::DynamicCast<RenderElement>(child));
146 } else if (child->GetType() == COMPOSED_ELEMENT) {
147 // If child is ComposedElement, just set apply function.
148 RefPtr<ComposedElement> composeChild = AceType::DynamicCast<ComposedElement>(child);
149 if (composeChild) {
150 composeChild->ApplyComposed(applyFunction_);
151 }
152 }
153 }
154
ApplyChildren()155 void ComposedElement::ApplyChildren()
156 {
157 for (const auto& child : children_) {
158 Apply(child);
159 }
160 }
161
Dump()162 void ComposedElement::Dump()
163 {
164 DumpLog::GetInstance().AddDesc("name:" + name_);
165 DumpLog::GetInstance().AddDesc("id:" + id_);
166 }
167
CanUpdate(const RefPtr<Component> & newComponent)168 bool ComposedElement::CanUpdate(const RefPtr<Component>& newComponent)
169 {
170 auto compose = AceType::DynamicCast<ComposedComponent>(newComponent);
171 if (!compose) {
172 return false;
173 }
174 if (compose->GetId() == id_) {
175 return true;
176 }
177
178 // For declarative, IDs MUST equal
179 auto context = context_.Upgrade();
180 if (context && context->GetIsDeclarative()) {
181 return false;
182 }
183
184 if (children_.empty()) {
185 return true;
186 }
187 auto childComponent = compose->GetChild();
188 if (!childComponent) {
189 return true;
190 }
191 return children_.front()->CanUpdate(childComponent);
192 }
193
NeedUpdateWithComponent(const RefPtr<Component> & newComponent)194 bool ComposedElement::NeedUpdateWithComponent(const RefPtr<Component>& newComponent)
195 {
196 auto component = AceType::DynamicCast<ComposedComponent>(newComponent);
197 if (component) {
198 auto newId = component->GetId();
199 if (newId.empty()) {
200 return true;
201 }
202
203 if (component->NeedUpdate()) {
204 return true;
205 }
206
207 auto context = context_.Upgrade();
208 if (context && context->GetIsDeclarative()) {
209 return newId == id_;
210 } else {
211 return newId != id_;
212 }
213 }
214 return true;
215 }
216
UnregisterForPartialUpdates()217 void ComposedElement::UnregisterForPartialUpdates()
218 {
219 LOGD("unregistering %{public}s(%{public}d) with %{public}d children.", AceType::TypeName(this), GetElementId(),
220 static_cast<int32_t>(GetChildren().size()));
221
222 if (HasRemoveFunction()) {
223 LOGD("... calling Remove function to Destroy JSView.");
224 CallRemoveFunction();
225 }
226
227 Element::UnregisterForPartialUpdates();
228 }
229
UpdateChild(const RefPtr<Element> & child,const RefPtr<Component> & newComponent)230 RefPtr<Element> ComposedElement::UpdateChild(const RefPtr<Element>& child, const RefPtr<Component>& newComponent)
231 {
232 auto context = context_.Upgrade();
233 if (!context) {
234 LOGW("Invalid pipeline stop updating");
235 return nullptr;
236 }
237
238 RefPtr<Element> newChild;
239 if (context->GetIsDeclarative()) {
240 newChild = UpdateChildWithSlot(child, newComponent, DEFAULT_ELEMENT_SLOT, GetRenderSlot());
241 } else {
242 newChild = UpdateChildWithSlot(child, newComponent, DEFAULT_ELEMENT_SLOT, DEFAULT_RENDER_SLOT);
243 }
244 countRenderNode_ = newChild ? newChild->CountRenderNode() : 0;
245 return newChild;
246 }
247
248 } // namespace OHOS::Ace
249