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 ()__anon7a9629650111::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
SplitHtml(std::shared_ptr<std::string> html)47 std::shared_ptr<PasteData> PasteboardWebController::SplitHtml(std::shared_ptr<std::string> html) noexcept
48 {
49 std::vector<std::pair<std::string, uint32_t>> matchVec = SplitHtmlWithImgLabel(html);
50 if (matchVec.empty()) {
51 return nullptr;
52 }
53 std::map<std::string, std::vector<uint8_t>> imgSrcMap = SplitHtmlWithImgSrcLabel(matchVec);
54 std::shared_ptr<PasteData> pasteData = BuildPasteData(html, imgSrcMap);
55 return pasteData;
56 }
57
RebuildHtml(std::shared_ptr<PasteData> pasteData)58 std::shared_ptr<std::string> PasteboardWebController::RebuildHtml(
59 std::shared_ptr<PasteData> pasteData) noexcept
60 {
61 std::vector<std::shared_ptr<PasteDataRecord>> pasteDataRecords = pasteData->AllRecords();
62 std::shared_ptr<std::string> htmlData;
63 std::map<uint32_t, std::pair<std::string, std::string>, Cmp> replaceUris;
64
65 for (auto& item : pasteDataRecords) {
66 std::shared_ptr<std::string> html = item->GetHtmlText();
67 if (html) {
68 htmlData = html;
69 }
70 std::shared_ptr<OHOS::Uri> uri = item->GetUri();
71 std::shared_ptr<MiscServices::MineCustomData> customData = item->GetCustomData();
72 if (!uri || !customData) {
73 continue;
74 }
75 std::map<std::string, std::vector<uint8_t>> customItemData = customData->GetItemData();
76 for (auto& itemData : customItemData) {
77 for (uint32_t i = 0; i < itemData.second.size(); i += FOUR_BYTES) {
78 uint32_t offset = static_cast<uint32_t>(itemData.second[i]) |
79 static_cast<uint32_t>(itemData.second[i + 1] << 8) |
80 static_cast<uint32_t>(itemData.second[i + 2] << 16) |
81 static_cast<uint32_t>(itemData.second[i + 3] << 24);
82 replaceUris[offset] = std::make_pair(uri->ToString(), itemData.first);
83 }
84 }
85 }
86
87 RemoveAllRecord(pasteData);
88 for (auto& replaceUri : replaceUris) {
89 htmlData->replace(replaceUri.first, replaceUri.second.second.size(), replaceUri.second.first);
90 }
91 pasteData->AddHtmlRecord(*htmlData);
92 return htmlData;
93 }
94
SplitHtmlWithImgLabel(const std::shared_ptr<std::string> html)95 std::vector<std::pair<std::string, uint32_t>> PasteboardWebController::SplitHtmlWithImgLabel(
96 const std::shared_ptr<std::string> html) noexcept
97 {
98 std::smatch results;
99 std::string pattern(IMG_TAG_PATTERN);
100 std::regex r(pattern);
101 std::string::const_iterator iterStart = html->begin();
102 std::string::const_iterator iterEnd = html->end();
103 std::vector<std::pair<std::string, uint32_t>> matchVec;
104
105 while (std::regex_search(iterStart, iterEnd, results, r)) {
106 std::string tmp = results[0];
107 iterStart = results[0].second;
108 uint32_t offset = static_cast<uint32_t>(results[0].first - html->begin());
109
110 matchVec.emplace_back(std::make_pair(tmp, offset));
111 }
112
113 return matchVec;
114 }
115
SplitHtmlWithImgSrcLabel(const std::vector<std::pair<std::string,uint32_t>> & matchVec)116 std::map<std::string, std::vector<uint8_t>> PasteboardWebController::SplitHtmlWithImgSrcLabel(
117 const std::vector<std::pair<std::string, uint32_t>>& matchVec) noexcept
118 {
119 std::map<std::string, std::vector<uint8_t>> res;
120 std::smatch match;
121 std::regex re(IMG_TAG_SRC_PATTERN);
122 for (auto& node : matchVec) {
123 std::string::const_iterator iterStart = node.first.begin();
124 std::string::const_iterator iterEnd = node.first.end();
125
126 while (std::regex_search(iterStart, iterEnd, match, re)) {
127 std::string tmp = match[0];
128 iterStart = match[0].second;
129 uint32_t offset = static_cast<uint32_t>(match[0].first - node.first.begin());
130 tmp = tmp.substr(IMG_TAG_SRC_HEAD.size());
131 tmp.pop_back();
132 if (!IsLocalURI(tmp)) {
133 continue;
134 }
135 offset += IMG_TAG_SRC_HEAD.size() + node.second;
136 for (uint32_t i = 0; i < FOUR_BYTES; i++) {
137 res[tmp].emplace_back((offset >> (EIGHT_BIT * i)) & 0xff);
138 }
139 }
140 }
141 return res;
142 }
143
BuildPasteData(std::shared_ptr<std::string> html,const std::map<std::string,std::vector<uint8_t>> & imgSrcMap)144 std::shared_ptr<PasteData> PasteboardWebController::BuildPasteData(
145 std::shared_ptr<std::string> html, const std::map<std::string, std::vector<uint8_t>>& imgSrcMap) noexcept
146 {
147 std::shared_ptr<PasteData> pasteData = std::make_shared<PasteData>();
148 pasteData->AddHtmlRecord(*html);
149 for (auto& item : imgSrcMap) {
150 PasteDataRecord::Builder builder(MiscServices::MIMETYPE_TEXT_URI);
151 auto uri = std::make_shared<OHOS::Uri>(item.first);
152 builder.SetUri(uri);
153 auto customData = std::make_shared<MiscServices::MineCustomData>();
154
155 customData->AddItemData(item.first, item.second);
156 builder.SetCustomData(customData);
157 auto record = builder.Build();
158 pasteData->AddRecord(record);
159 }
160 return pasteData;
161 }
162
RemoveAllRecord(std::shared_ptr<PasteData> pasteData)163 void PasteboardWebController::RemoveAllRecord(std::shared_ptr<PasteData> pasteData) noexcept
164 {
165 std::size_t recordCount = pasteData->GetRecordCount();
166 for (uint32_t i = 0; i < recordCount; i++) {
167 if (!pasteData->RemoveRecordAt(0)) {
168 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "WebClipboardController RemoveRecord failed, i=%{public}u", i);
169 }
170 }
171 }
172
IsLocalURI(std::string & uri)173 bool PasteboardWebController::IsLocalURI(std::string& uri) noexcept
174 {
175 return uri.substr(0, IMG_LOCAL_URI.size()) == IMG_LOCAL_URI || uri.find(IMG_LOCAL_PATH) == std::string::npos;
176 }
177 } // namespace MiscServices
178 } // namespace OHOS
179