UNPKG

8.6 kBtext/x-cView Raw
1// Copyright (c) Facebook, Inc. and its affiliates.
2
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6#include "Differentiator.h"
7
8#include <better/map.h>
9#include <react/core/LayoutableShadowNode.h>
10#include <react/debug/SystraceSection.h>
11#include "ShadowView.h"
12
13namespace facebook {
14namespace react {
15
16static void sliceChildShadowNodeViewPairsRecursively(
17 ShadowViewNodePairList &pairList,
18 Point layoutOffset,
19 const ShadowNode &shadowNode) {
20 for (const auto &childShadowNode : shadowNode.getChildren()) {
21 auto shadowView = ShadowView(*childShadowNode);
22
23 const auto layoutableShadowNode =
24 dynamic_cast<const LayoutableShadowNode *>(childShadowNode.get());
25#ifndef ANDROID
26 // New approach (iOS):
27 // Non-view components are treated as layout-only views (they aren't
28 // represented as `ShadowView`s).
29 if (!layoutableShadowNode || layoutableShadowNode->isLayoutOnly()) {
30#else
31 // Previous approach (Android):
32 // Non-view components are treated as normal views with an empty layout
33 // (they are represented as `ShadowView`s).
34 if (layoutableShadowNode && layoutableShadowNode->isLayoutOnly()) {
35#endif
36 sliceChildShadowNodeViewPairsRecursively(
37 pairList,
38 layoutOffset + shadowView.layoutMetrics.frame.origin,
39 *childShadowNode);
40 } else {
41 shadowView.layoutMetrics.frame.origin += layoutOffset;
42 pairList.push_back({shadowView, *childShadowNode});
43 }
44 }
45}
46
47static ShadowViewNodePairList sliceChildShadowNodeViewPairs(
48 const ShadowNode &shadowNode) {
49 ShadowViewNodePairList pairList;
50 sliceChildShadowNodeViewPairsRecursively(pairList, {0, 0}, shadowNode);
51 return pairList;
52}
53
54static void calculateShadowViewMutations(
55 ShadowViewMutationList &mutations,
56 const ShadowView &parentShadowView,
57 const ShadowViewNodePairList &oldChildPairs,
58 const ShadowViewNodePairList &newChildPairs) {
59 // The current version of the algorithm is otimized for simplicity,
60 // not for performance or optimal result.
61
62 if (oldChildPairs == newChildPairs) {
63 return;
64 }
65
66 if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
67 return;
68 }
69
70 better::map<Tag, ShadowViewNodePair> insertedPairs;
71 int index = 0;
72
73 ShadowViewMutationList createMutations = {};
74 ShadowViewMutationList deleteMutations = {};
75 ShadowViewMutationList insertMutations = {};
76 ShadowViewMutationList removeMutations = {};
77 ShadowViewMutationList updateMutations = {};
78 ShadowViewMutationList downwardMutations = {};
79 ShadowViewMutationList destructiveDownwardMutations = {};
80
81 // Stage 1: Collecting `Update` mutations
82 for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
83 index++) {
84 const auto &oldChildPair = oldChildPairs[index];
85 const auto &newChildPair = newChildPairs[index];
86
87 if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
88 // Totally different nodes, updating is impossible.
89 break;
90 }
91
92 if (oldChildPair.shadowView != newChildPair.shadowView) {
93 updateMutations.push_back(ShadowViewMutation::UpdateMutation(
94 parentShadowView,
95 oldChildPair.shadowView,
96 newChildPair.shadowView,
97 index));
98 }
99
100 const auto oldGrandChildPairs =
101 sliceChildShadowNodeViewPairs(oldChildPair.shadowNode);
102 const auto newGrandChildPairs =
103 sliceChildShadowNodeViewPairs(newChildPair.shadowNode);
104 calculateShadowViewMutations(
105 *(newGrandChildPairs.size() ? &downwardMutations
106 : &destructiveDownwardMutations),
107 oldChildPair.shadowView,
108 oldGrandChildPairs,
109 newGrandChildPairs);
110 }
111
112 int lastIndexAfterFirstStage = index;
113
114 // Stage 2: Collecting `Insert` mutations
115 for (; index < newChildPairs.size(); index++) {
116 const auto &newChildPair = newChildPairs[index];
117
118 insertMutations.push_back(ShadowViewMutation::InsertMutation(
119 parentShadowView, newChildPair.shadowView, index));
120
121 insertedPairs.insert({newChildPair.shadowView.tag, newChildPair});
122 }
123
124 // Stage 3: Collecting `Delete` and `Remove` mutations
125 for (index = lastIndexAfterFirstStage; index < oldChildPairs.size();
126 index++) {
127 const auto &oldChildPair = oldChildPairs[index];
128
129 // Even if the old view was (re)inserted, we have to generate `remove`
130 // mutation.
131 removeMutations.push_back(ShadowViewMutation::RemoveMutation(
132 parentShadowView, oldChildPair.shadowView, index));
133
134 const auto &it = insertedPairs.find(oldChildPair.shadowView.tag);
135
136 if (it == insertedPairs.end()) {
137 // The old view was *not* (re)inserted.
138 // We have to generate `delete` mutation and apply the algorithm
139 // recursively.
140 deleteMutations.push_back(
141 ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
142
143 // We also have to call the algorithm recursively to clean up the entire
144 // subtree starting from the removed view.
145 calculateShadowViewMutations(
146 destructiveDownwardMutations,
147 oldChildPair.shadowView,
148 sliceChildShadowNodeViewPairs(oldChildPair.shadowNode),
149 {});
150 } else {
151 // The old view *was* (re)inserted.
152 // We have to call the algorithm recursively if the inserted view
153 // is *not* the same as removed one.
154 const auto &newChildPair = it->second;
155 if (newChildPair.shadowView != oldChildPair.shadowView) {
156 const auto oldGrandChildPairs =
157 sliceChildShadowNodeViewPairs(oldChildPair.shadowNode);
158 const auto newGrandChildPairs =
159 sliceChildShadowNodeViewPairs(newChildPair.shadowNode);
160 calculateShadowViewMutations(
161 *(newGrandChildPairs.size() ? &downwardMutations
162 : &destructiveDownwardMutations),
163 newChildPair.shadowView,
164 oldGrandChildPairs,
165 newGrandChildPairs);
166 }
167
168 // In any case we have to remove the view from `insertedPairs` as
169 // indication that the view was actually removed (which means that
170 // the view existed before), hence we don't have to generate
171 // `create` mutation.
172 insertedPairs.erase(it);
173 }
174 }
175
176 // Stage 4: Collecting `Create` mutations
177 for (index = lastIndexAfterFirstStage; index < newChildPairs.size();
178 index++) {
179 const auto &newChildPair = newChildPairs[index];
180
181 if (insertedPairs.find(newChildPair.shadowView.tag) ==
182 insertedPairs.end()) {
183 // The new view was (re)inserted, so there is no need to create it.
184 continue;
185 }
186
187 createMutations.push_back(
188 ShadowViewMutation::CreateMutation(newChildPair.shadowView));
189
190 calculateShadowViewMutations(
191 downwardMutations,
192 newChildPair.shadowView,
193 {},
194 sliceChildShadowNodeViewPairs(newChildPair.shadowNode));
195 }
196
197 // All mutations in an optimal order:
198 mutations.insert(
199 mutations.end(),
200 destructiveDownwardMutations.begin(),
201 destructiveDownwardMutations.end());
202 mutations.insert(
203 mutations.end(), updateMutations.begin(), updateMutations.end());
204 mutations.insert(
205 mutations.end(), removeMutations.rbegin(), removeMutations.rend());
206 mutations.insert(
207 mutations.end(), deleteMutations.begin(), deleteMutations.end());
208 mutations.insert(
209 mutations.end(), createMutations.begin(), createMutations.end());
210 mutations.insert(
211 mutations.end(), insertMutations.begin(), insertMutations.end());
212 mutations.insert(
213 mutations.end(), downwardMutations.begin(), downwardMutations.end());
214}
215
216ShadowViewMutationList calculateShadowViewMutations(
217 const ShadowNode &oldRootShadowNode,
218 const ShadowNode &newRootShadowNode) {
219 SystraceSection s("calculateShadowViewMutations");
220
221 // Root shadow nodes must have same tag.
222 assert(oldRootShadowNode.getTag() == newRootShadowNode.getTag());
223
224 ShadowViewMutationList mutations;
225
226 auto oldRootShadowView = ShadowView(oldRootShadowNode);
227 auto newRootShadowView = ShadowView(newRootShadowNode);
228
229 if (oldRootShadowView != newRootShadowView) {
230 mutations.push_back(ShadowViewMutation::UpdateMutation(
231 ShadowView(), oldRootShadowView, newRootShadowView, -1));
232 }
233
234 calculateShadowViewMutations(
235 mutations,
236 ShadowView(oldRootShadowNode),
237 sliceChildShadowNodeViewPairs(oldRootShadowNode),
238 sliceChildShadowNodeViewPairs(newRootShadowNode));
239
240 return mutations;
241}
242
243} // namespace react
244} // namespace facebook