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