1 /*
2 * Copyright (C) 2023 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 <regex>
17 #include "file_uri.h"
18 #include "pasteboard_web_controller.h"
19
20 namespace {
21 const std::string IMG_TAG_PATTERN = "<img.*?>";
22 const std::string IMG_TAG_SRC_PATTERN = "src=(['\"])(.*?)\\1";
23 const std::string IMG_TAG_SRC_HEAD = "src=\"";
24 const std::string IMG_LOCAL_URI = "file:///";
25 const std::string IMG_LOCAL_PATH = "://";
26 constexpr uint32_t FOUR_BYTES = 4;
27 constexpr uint32_t EIGHT_BIT = 8;
28
29 struct Cmp {
operator ()__anon5f8adb9d0110::Cmp30 bool operator()(const uint32_t& lhs, const uint32_t& rhs) const
31 {
32 return lhs > rhs;
33 }
34 };
35 } // namespace
36
37 namespace OHOS {
38 namespace MiscServices {
39
40 // static
GetInstance()41 PasteboardWebController& PasteboardWebController::GetInstance()
42 {
43 static PasteboardWebController instance;
44 return instance;
45 }
46
SplitHtml2Records(const std::shared_ptr<std::string> & html,uint32_t recordId)47 std::vector<std::shared_ptr<PasteDataRecord>> PasteboardWebController::SplitHtml2Records(
48 const std::shared_ptr<std::string> &html, uint32_t recordId) noexcept
49 {
50 std::vector<std::pair<std::string, uint32_t>> matchVec = SplitHtmlWithImgLabel(html);
51 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "matchVec size: %{public}zu", matchVec.size());
52 if (matchVec.empty()) {
53 return {};
54 }
55 std::map<std::string, std::vector<uint8_t>> imgSrcMap = SplitHtmlWithImgSrcLabel(matchVec);
56 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "imgSrcMap size: %{public}zu", imgSrcMap.size());
57 return BuildPasteDataRecords(imgSrcMap, recordId);
58 }
59
MergeExtraUris2Html(PasteData & data)60 void PasteboardWebController::MergeExtraUris2Html(PasteData &data)
61 {
62 auto recordGroups = GroupRecordWithFrom(data);
63 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "recordGroups size: %{public}zu", recordGroups.size());
64 for (auto &recordGroup : recordGroups) {
65 ReplaceHtmlRecordContentByExtraUris(recordGroup.second);
66 }
67 RemoveExtraUris(data);
68 }
69
RebuildHtml(std::shared_ptr<PasteData> pasteData)70 std::shared_ptr<std::string> PasteboardWebController::RebuildHtml(
71 std::shared_ptr<PasteData> pasteData) noexcept
72 {
73 std::vector<std::shared_ptr<PasteDataRecord>> pasteDataRecords = pasteData->AllRecords();
74 std::shared_ptr<std::string> htmlData;
75 std::map<uint32_t, std::pair<std::string, std::string>, Cmp> replaceUris;
76
77 for (auto& item : pasteDataRecords) {
78 std::shared_ptr<std::string> html = item->GetHtmlText();
79 if (html) {
80 htmlData = html;
81 }
82 std::shared_ptr<OHOS::Uri> uri = item->GetUri();
83 std::shared_ptr<MiscServices::MineCustomData> customData = item->GetCustomData();
84 if (!uri || !customData) {
85 continue;
86 }
87 std::map<std::string, std::vector<uint8_t>> customItemData = customData->GetItemData();
88 for (auto& itemData : customItemData) {
89 for (uint32_t i = 0; i < itemData.second.size(); i += FOUR_BYTES) {
90 uint32_t offset = static_cast<uint32_t>(itemData.second[i]) |
91 static_cast<uint32_t>(itemData.second[i + 1] << 8) |
92 static_cast<uint32_t>(itemData.second[i + 2] << 16) |
93 static_cast<uint32_t>(itemData.second[i + 3] << 24);
94 replaceUris[offset] = std::make_pair(uri->ToString(), itemData.first);
95 }
96 }
97 }
98
99 RemoveAllRecord(pasteData);
100 for (auto& replaceUri : replaceUris) {
101 htmlData->replace(replaceUri.first, replaceUri.second.second.size(), replaceUri.second.first);
102 }
103 pasteData->AddHtmlRecord(*htmlData);
104 return htmlData;
105 }
106
SplitHtmlWithImgLabel(const std::shared_ptr<std::string> html)107 std::vector<std::pair<std::string, uint32_t>> PasteboardWebController::SplitHtmlWithImgLabel(
108 const std::shared_ptr<std::string> html) noexcept
109 {
110 std::smatch results;
111 std::string pattern(IMG_TAG_PATTERN);
112 std::regex r(pattern);
113 std::string::const_iterator iterStart = html->begin();
114 std::string::const_iterator iterEnd = html->end();
115 std::vector<std::pair<std::string, uint32_t>> matchVec;
116
117 while (std::regex_search(iterStart, iterEnd, results, r)) {
118 std::string tmp = results[0];
119 iterStart = results[0].second;
120 uint32_t offset = static_cast<uint32_t>(results[0].first - html->begin());
121
122 matchVec.emplace_back(tmp, offset);
123 }
124
125 return matchVec;
126 }
127
SplitHtmlWithImgSrcLabel(const std::vector<std::pair<std::string,uint32_t>> & matchVec)128 std::map<std::string, std::vector<uint8_t>> PasteboardWebController::SplitHtmlWithImgSrcLabel(
129 const std::vector<std::pair<std::string, uint32_t>>& matchVec) noexcept
130 {
131 std::map<std::string, std::vector<uint8_t>> res;
132 std::smatch match;
133 std::regex re(IMG_TAG_SRC_PATTERN);
134 for (auto& node : matchVec) {
135 std::string::const_iterator iterStart = node.first.begin();
136 std::string::const_iterator iterEnd = node.first.end();
137
138 while (std::regex_search(iterStart, iterEnd, match, re)) {
139 std::string tmp = match[0];
140 iterStart = match[0].second;
141 uint32_t offset = static_cast<uint32_t>(match[0].first - node.first.begin());
142 tmp = tmp.substr(IMG_TAG_SRC_HEAD.size());
143 tmp.pop_back();
144 if (!IsLocalURI(tmp)) {
145 continue;
146 }
147 offset += IMG_TAG_SRC_HEAD.size() + node.second;
148 for (uint32_t i = 0; i < FOUR_BYTES; i++) {
149 res[tmp].emplace_back((offset >> (EIGHT_BIT * i)) & 0xff);
150 }
151 }
152 }
153 return res;
154 }
155
BuildPasteDataRecords(const std::map<std::string,std::vector<uint8_t>> & imgSrcMap,uint32_t recordId)156 std::vector<std::shared_ptr<PasteDataRecord>> PasteboardWebController::BuildPasteDataRecords(
157 const std::map<std::string, std::vector<uint8_t>> &imgSrcMap, uint32_t recordId) noexcept
158 {
159 std::vector<std::shared_ptr<PasteDataRecord>> records;
160 for (auto &item : imgSrcMap) {
161 PasteDataRecord::Builder builder(MiscServices::MIMETYPE_TEXT_URI);
162 auto uri = std::make_shared<OHOS::Uri>(item.first);
163 builder.SetUri(uri);
164 auto customData = std::make_shared<MiscServices::MineCustomData>();
165
166 customData->AddItemData(item.first, item.second);
167 builder.SetCustomData(customData);
168 auto record = builder.Build();
169 record->SetFrom(recordId);
170 records.push_back(record);
171 }
172 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "Build extra records size: %{public}zu", records.size());
173 return records;
174 }
175
RemoveRecordById(PasteData & pasteData,uint32_t recordId)176 void PasteboardWebController::RemoveRecordById(PasteData &pasteData, uint32_t recordId) noexcept
177 {
178 for (uint32_t i = 0; i < pasteData.GetRecordCount(); i++) {
179 if (pasteData.GetRecordAt(i)->GetRecordId() == recordId) {
180 if (pasteData.RemoveRecordAt(i)) {
181 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT,
182 "WebClipboardController RemoveRecord success, i=%{public}u", i);
183 return;
184 }
185 PASTEBOARD_HILOGW(PASTEBOARD_MODULE_CLIENT,
186 "WebClipboardController RemoveRecord failed, i=%{public}u", i);
187 }
188 }
189 }
190
RemoveAllRecord(std::shared_ptr<PasteData> pasteData)191 void PasteboardWebController::RemoveAllRecord(std::shared_ptr<PasteData> pasteData) noexcept
192 {
193 std::size_t recordCount = pasteData->GetRecordCount();
194 for (uint32_t i = 0; i < recordCount; i++) {
195 if (!pasteData->RemoveRecordAt(0)) {
196 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "WebClipboardController RemoveRecord failed, i=%{public}u", i);
197 }
198 }
199 }
200
IsLocalURI(std::string & uri)201 bool PasteboardWebController::IsLocalURI(std::string& uri) noexcept
202 {
203 return uri.substr(0, IMG_LOCAL_URI.size()) == IMG_LOCAL_URI || uri.find(IMG_LOCAL_PATH) == std::string::npos;
204 }
205
ReplaceHtmlRecordContentByExtraUris(std::vector<std::shared_ptr<PasteDataRecord>> & records)206 void PasteboardWebController::ReplaceHtmlRecordContentByExtraUris(
207 std::vector<std::shared_ptr<PasteDataRecord>> &records)
208 {
209 std::shared_ptr<PasteDataRecord> htmlRecord = nullptr;
210 std::shared_ptr<std::string> htmlData;
211 std::map<uint32_t, std::pair<std::string, std::string>, Cmp> replaceUris;
212 for (const auto &item : records) {
213 auto htmlEntry = item->GetEntryByMimeType(MIMETYPE_TEXT_HTML);
214 if (htmlEntry != nullptr) {
215 auto html = htmlEntry->ConvertToHtml();
216 if (html != nullptr && !html->empty()) {
217 htmlData = html;
218 htmlRecord = item;
219 continue;
220 }
221 }
222 std::shared_ptr<OHOS::Uri> uri = item->GetUri();
223 std::shared_ptr<MiscServices::MineCustomData> customData = item->GetCustomData();
224 if (!uri || !customData) {
225 continue;
226 }
227 std::map<std::string, std::vector<uint8_t>> customItemData = customData->GetItemData();
228 for (auto &itemData : customItemData) {
229 for (uint32_t i = 0; i < itemData.second.size(); i += FOUR_BYTES) {
230 uint32_t offset = static_cast<uint32_t>(itemData.second[i]) |
231 static_cast<uint32_t>(itemData.second[i + 1] << 8) |
232 static_cast<uint32_t>(itemData.second[i + 2] << 16) |
233 static_cast<uint32_t>(itemData.second[i + 3] << 24);
234 replaceUris[offset] = std::make_pair(uri->ToString(), itemData.first);
235 }
236 }
237 }
238 if (htmlData == nullptr) {
239 PASTEBOARD_HILOGW(PASTEBOARD_MODULE_CLIENT, "htmlData is nullptr");
240 return;
241 }
242
243 for (const auto &replaceUri : replaceUris) {
244 htmlData->replace(replaceUri.first, replaceUri.second.second.size(), replaceUri.second.first);
245 }
246 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "replace uri count: %{public}zu", replaceUris.size());
247 if (htmlRecord != nullptr) {
248 auto htmlUtdId = CommonUtils::Convert2UtdId(UDMF::UDType::UD_BUTT, MIMETYPE_TEXT_HTML);
249 auto newHtmlEntry = std::make_shared<PasteDataEntry>(htmlUtdId, *htmlData);
250 htmlRecord->AddEntryByMimeType(MIMETYPE_TEXT_HTML, newHtmlEntry);
251 htmlRecord->SetFrom(0);
252 }
253 }
254
GroupRecordWithFrom(PasteData & data)255 std::map<std::uint32_t, std::vector<std::shared_ptr<PasteDataRecord>>> PasteboardWebController::GroupRecordWithFrom(
256 PasteData &data)
257 {
258 std::map<std::uint32_t, std::vector<std::shared_ptr<PasteDataRecord>>> groupMap;
259 for (const auto &record : data.AllRecords()) {
260 if (record->GetFrom() == 0) {
261 continue;
262 }
263 auto item = groupMap.find(record->GetFrom());
264 auto value = item != groupMap.end() ? item->second : std::vector<std::shared_ptr<PasteDataRecord>>();
265 value.emplace_back(record);
266 groupMap.insert_or_assign(record->GetFrom(), value);
267 }
268 return groupMap;
269 }
270
RemoveExtraUris(PasteData & data)271 void PasteboardWebController::RemoveExtraUris(PasteData &data)
272 {
273 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "Before remove record count: %{public}zu", data.AllRecords().size());
274 for (const auto &record : data.AllRecords()) {
275 if (record->GetFrom() > 0 && record->GetMimeType() == MIMETYPE_TEXT_URI) {
276 RemoveRecordById(data, record->GetRecordId());
277 }
278 }
279 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "After remove record count: %{public}zu", data.AllRecords().size());
280 }
281 } // namespace MiscServices
282 } // namespace OHOS
283