• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "pasteboard_client_adapter_impl.h"
17 
18 #include <mutex>
19 #include <securec.h>
20 
21 #include "hisysevent_adapter.h"
22 #include "media_errors.h"
23 #include "nweb_log.h"
24 #include "ohos_adapter_helper.h"
25 #include "pasteboard_error.h"
26 #include "remote_uri.h"
27 
28 using namespace OHOS::MiscServices;
29 using namespace OHOS::DistributedFS::ModuleRemoteUri;
30 
31 namespace OHOS::NWeb {
32 constexpr char PASTE_BOARD_ERROR[] = "PASTE_BOARD_ERROR";
33 constexpr char ERROR_CODE[] = "ERROR_CODE";
34 constexpr char RECORD_SIZE[] = "RECORD_SIZE";
35 constexpr char DATA_TYPE[] = "DATA_TYPE";
36 constexpr char MIMETYPE_HYBRID[] = "hybrid";
37 constexpr char MIMETYPE_NULL[] = "null";
38 
PasteboardObserverAdapterImpl(std::shared_ptr<PasteboardObserverAdapter> observer)39 PasteboardObserverAdapterImpl::PasteboardObserverAdapterImpl(
40     std::shared_ptr<PasteboardObserverAdapter> observer)
41     : observer_(observer) {}
42 
OnPasteboardChanged()43 void PasteboardObserverAdapterImpl::OnPasteboardChanged()
44 {
45     if (observer_) {
46         observer_->OnPasteboardChanged();
47     }
48 }
49 
PasteDataRecordAdapterImpl(std::shared_ptr<PasteDataRecord> record)50 PasteDataRecordAdapterImpl::PasteDataRecordAdapterImpl(
51     std::shared_ptr<PasteDataRecord> record)
52     : record_(record) {}
53 
PasteDataRecordAdapterImpl(const std::string & mimeType)54 PasteDataRecordAdapterImpl::PasteDataRecordAdapterImpl(
55     const std::string& mimeType)
56 {
57     builder_ = std::make_shared<PasteDataRecord::Builder>(mimeType);
58     if (builder_) {
59         record_ = builder_->Build();
60     }
61 }
62 
PasteDataRecordAdapterImpl(const std::string & mimeType,std::shared_ptr<std::string> htmlText,std::shared_ptr<std::string> plainText)63 PasteDataRecordAdapterImpl::PasteDataRecordAdapterImpl(
64     const std::string& mimeType,
65     std::shared_ptr<std::string> htmlText,
66     std::shared_ptr<std::string> plainText)
67 {
68     record_ = std::make_shared<PasteDataRecord>(mimeType,
69                                                 htmlText,
70                                                 nullptr,
71                                                 plainText,
72                                                 nullptr);
73 }
74 
NewRecord(const std::string & mimeType)75 std::shared_ptr<PasteDataRecordAdapter> PasteDataRecordAdapter::NewRecord(
76     const std::string& mimeType)
77 {
78     return std::make_shared<PasteDataRecordAdapterImpl>(mimeType);
79 }
80 
NewRecord(const std::string & mimeType,std::shared_ptr<std::string> htmlText,std::shared_ptr<std::string> plainText)81 std::shared_ptr<PasteDataRecordAdapter> PasteDataRecordAdapter::NewRecord(
82     const std::string& mimeType,
83     std::shared_ptr<std::string> htmlText,
84     std::shared_ptr<std::string> plainText)
85 {
86     return std::make_shared<PasteDataRecordAdapterImpl>(mimeType,
87                                                         htmlText,
88                                                         plainText);
89 }
90 
SetHtmlText(std::shared_ptr<std::string> htmlText)91 bool PasteDataRecordAdapterImpl::SetHtmlText(std::shared_ptr<std::string> htmlText)
92 {
93     if (builder_) {
94         record_ = builder_->SetHtmlText(htmlText).Build();
95         return true;
96     }
97     WVLOG_E("record_ is null");
98     return false;
99 }
100 
SetPlainText(std::shared_ptr<std::string> plainText)101 bool PasteDataRecordAdapterImpl::SetPlainText(std::shared_ptr<std::string> plainText)
102 {
103     if (builder_) {
104         record_ = builder_->SetPlainText(plainText).Build();
105         return true;
106     }
107     WVLOG_E("record_ is null");
108     return false;
109 }
110 
SetUri(const std::string & uriString)111 bool PasteDataRecordAdapterImpl::SetUri(const std::string& uriString)
112 {
113     if (uriString.empty() || !builder_) {
114         WVLOG_E("record_ or uriString is null");
115         return false;
116     }
117     std::shared_ptr<OHOS::Uri> uri = std::make_shared<OHOS::Uri>(uriString);
118     record_ = builder_->SetUri(uri).Build();
119     return true;
120 }
121 
SetCustomData(PasteCustomData & data)122 bool PasteDataRecordAdapterImpl::SetCustomData(PasteCustomData& data)
123 {
124     if (data.empty() || !builder_) {
125         WVLOG_E("custom data is empty or builder_ is null");
126         return false;
127     }
128     std::shared_ptr<MineCustomData> customData =
129         std::make_shared<MineCustomData>();
130     for (PasteCustomData::iterator iter = data.begin(); iter != data.end(); ++iter) {
131         customData->AddItemData(iter->first, iter->second);
132     }
133     record_ = builder_->SetCustomData(customData).Build();
134     return true;
135 }
136 
ImageToClipboardAlphaType(const Media::ImageInfo & imgInfo)137 ClipBoardImageAlphaType PasteDataRecordAdapterImpl::ImageToClipboardAlphaType
138     (const Media::ImageInfo &imgInfo)
139 {
140     switch (imgInfo.alphaType) {
141         case Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN :
142             return ClipBoardImageAlphaType::ALPHA_TYPE_UNKNOWN;
143         case Media::AlphaType::IMAGE_ALPHA_TYPE_OPAQUE :
144             return ClipBoardImageAlphaType::ALPHA_TYPE_OPAQUE;
145         case Media::AlphaType::IMAGE_ALPHA_TYPE_PREMUL :
146             return ClipBoardImageAlphaType::ALPHA_TYPE_PREMULTIPLIED;
147         default :
148             return ClipBoardImageAlphaType::ALPHA_TYPE_UNKNOWN;
149     }
150 }
151 
ImageToClipboardColorType(const Media::ImageInfo & imgInfo)152 ClipBoardImageColorType PasteDataRecordAdapterImpl::ImageToClipboardColorType
153     (const Media::ImageInfo &imgInfo)
154 {
155     switch (imgInfo.pixelFormat) {
156         case Media::PixelFormat::RGBA_8888 :
157             return ClipBoardImageColorType::COLOR_TYPE_RGBA_8888;
158         case Media::PixelFormat::BGRA_8888 :
159             return ClipBoardImageColorType::COLOR_TYPE_BGRA_8888;
160         default :
161             return ClipBoardImageColorType::COLOR_TYPE_UNKNOWN;
162     }
163 }
164 
ClipboardToImageAlphaType(ClipBoardImageAlphaType alphaType)165 Media::AlphaType PasteDataRecordAdapterImpl::ClipboardToImageAlphaType
166     (ClipBoardImageAlphaType alphaType)
167 {
168     switch (alphaType) {
169         case ClipBoardImageAlphaType::ALPHA_TYPE_UNKNOWN :
170             return Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN;
171         case ClipBoardImageAlphaType::ALPHA_TYPE_OPAQUE :
172             return Media::AlphaType::IMAGE_ALPHA_TYPE_OPAQUE;
173         case ClipBoardImageAlphaType::ALPHA_TYPE_PREMULTIPLIED :
174             return Media::AlphaType::IMAGE_ALPHA_TYPE_PREMUL;
175         default :
176             return Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN;
177     }
178 }
179 
ClipboardToImageColorType(ClipBoardImageColorType colorType)180 Media::PixelFormat PasteDataRecordAdapterImpl::ClipboardToImageColorType
181     (ClipBoardImageColorType colorType)
182 {
183     switch (colorType) {
184         case ClipBoardImageColorType::COLOR_TYPE_RGBA_8888 :
185             return Media::PixelFormat::RGBA_8888;
186         case ClipBoardImageColorType::COLOR_TYPE_BGRA_8888 :
187             return Media::PixelFormat::BGRA_8888;
188         default :
189             return Media::PixelFormat::UNKNOWN;
190     }
191 }
192 
SetImgData(std::shared_ptr<ClipBoardImageDataAdapter> imageData)193 bool PasteDataRecordAdapterImpl::SetImgData(std::shared_ptr<ClipBoardImageDataAdapter> imageData)
194 {
195     if (imageData == nullptr) {
196         WVLOG_E("imageData is null");
197         return false;
198     }
199     Media::InitializationOptions opt;
200     opt.size.width = imageData->GetWidth();
201     opt.size.height = imageData->GetHeight();
202     opt.pixelFormat = ClipboardToImageColorType(imageData->GetColorType());
203     opt.alphaType = ClipboardToImageAlphaType(imageData->GetAlphaType());
204     opt.editable = true;
205     std::unique_ptr<Media::PixelMap> pixelMap = Media::PixelMap::Create(opt);
206     if (pixelMap == nullptr) {
207         WVLOG_E("create pixel map failed");
208         return false;
209     }
210     uint64_t stride = static_cast<uint64_t>(imageData->GetWidth()) << 2;
211     uint64_t bufferSize = stride * static_cast<uint64_t>(imageData->GetHeight());
212     uint32_t ret = pixelMap->WritePixels(reinterpret_cast<const uint8_t *>(imageData->GetData()), bufferSize);
213     if (ret != Media::SUCCESS) {
214         WVLOG_E("write pixel map failed %{public}u", ret);
215         return false;
216     }
217 
218     std::shared_ptr<Media::PixelMap> pixelMapIn = move(pixelMap);
219 
220     if (!builder_) {
221         WVLOG_E("record_ is null");
222         return false;
223     }
224     record_ = builder_->SetPixelMap(pixelMapIn).Build();
225     return true;
226 }
227 
GetMimeType()228 std::string PasteDataRecordAdapterImpl::GetMimeType()
229 {
230     return (record_ != nullptr) ? record_->GetMimeType() : "";
231 }
232 
GetHtmlText()233 std::shared_ptr<std::string> PasteDataRecordAdapterImpl::GetHtmlText()
234 {
235     return (record_ != nullptr) ? record_->GetHtmlText() : nullptr;
236 }
237 
GetPlainText()238 std::shared_ptr<std::string> PasteDataRecordAdapterImpl::GetPlainText()
239 {
240     return (record_ != nullptr) ? record_->GetPlainText() : nullptr;
241 }
242 
GetImgData(std::shared_ptr<ClipBoardImageDataAdapter> imageData)243 bool PasteDataRecordAdapterImpl::GetImgData(std::shared_ptr<ClipBoardImageDataAdapter> imageData)
244 {
245     if (record_ == nullptr) {
246         WVLOG_E("record_ is null");
247         return false;
248     }
249 
250     if (imageData == nullptr) {
251         WVLOG_E("imageData is null");
252         return false;
253     }
254 
255     std::shared_ptr<Media::PixelMap> pixelMap = record_->GetPixelMap();
256     if (pixelMap == nullptr) {
257         WVLOG_E("pixelMap is null");
258         return false;
259     }
260 
261     Media::ImageInfo imgInfo;
262     ClearImgBuffer();
263     bufferSize_ = pixelMap->GetCapacity();
264     if ((bufferSize_ == 0) || (pixelMap->GetPixels() == nullptr)) {
265         WVLOG_E("data in pixel map is empty");
266         return false;
267     }
268 
269     imgBuffer_ = static_cast<uint8_t *>(calloc(static_cast<size_t>(bufferSize_), sizeof(uint8_t)));
270     if (imgBuffer_ == nullptr) {
271         WVLOG_E("calloc imgbuffer failed");
272         return false;
273     }
274 
275     if (memcpy_s(imgBuffer_, bufferSize_, pixelMap->GetPixels(), bufferSize_)) {
276         WVLOG_E("memcpy imgbuffer failed");
277         ClearImgBuffer();
278         return false;
279     }
280 
281     int32_t width = pixelMap->GetWidth();
282     int32_t height = pixelMap->GetHeight();
283     pixelMap->GetImageInfo(imgInfo);
284     int32_t rowBytes = pixelMap->GetRowBytes();
285 
286     imageData->SetColorType(ImageToClipboardColorType(imgInfo));
287     imageData->SetAlphaType(ImageToClipboardAlphaType(imgInfo));
288     imageData->SetData((uint32_t *)(imgBuffer_));
289     imageData->SetDataSize(static_cast<size_t>(bufferSize_));
290     imageData->SetWidth(width);
291     imageData->SetHeight(height);
292     imageData->SetRowBytes(static_cast<size_t>(rowBytes));
293     return true;
294 }
295 
GetUri()296 std::shared_ptr<std::string> PasteDataRecordAdapterImpl::GetUri()
297 {
298     if (record_ == nullptr) {
299         return nullptr;
300     }
301     auto uri = record_->GetUri();
302     if (uri == nullptr) {
303         return nullptr;
304     }
305     return std::make_shared<std::string>(uri->ToString());
306 }
307 
GetCustomData()308 std::shared_ptr<PasteCustomData> PasteDataRecordAdapterImpl::GetCustomData()
309 {
310     if (record_ == nullptr) {
311         return nullptr;
312     }
313     auto customData = record_->GetCustomData();
314     if (customData == nullptr) {
315         return nullptr;
316     }
317     return std::make_shared<PasteCustomData>(customData->GetItemData());
318 }
319 
ClearImgBuffer()320 void PasteDataRecordAdapterImpl::ClearImgBuffer()
321 {
322     if (imgBuffer_) {
323         free(imgBuffer_);
324         imgBuffer_ = nullptr;
325         bufferSize_ = 0;
326     }
327 }
328 
Clear()329 void PasteDataRecordAdapterImpl::Clear()
330 {
331     ClearImgBuffer();
332 }
333 
GetRecord()334 std::shared_ptr<PasteDataRecord> PasteDataRecordAdapterImpl::GetRecord()
335 {
336     return record_;
337 }
338 
PasteDataAdapterImpl()339 PasteDataAdapterImpl::PasteDataAdapterImpl()
340     : data_(std::make_shared<PasteData>()) {}
341 
PasteDataAdapterImpl(std::shared_ptr<PasteData> data)342 PasteDataAdapterImpl::PasteDataAdapterImpl(
343     std::shared_ptr<PasteData> data) : data_(data) {}
344 
AddHtmlRecord(const std::string & html)345 void PasteDataAdapterImpl::AddHtmlRecord(const std::string& html)
346 {
347     if (data_ != nullptr) {
348         data_->AddHtmlRecord(html);
349     }
350 }
351 
AddTextRecord(const std::string & text)352 void PasteDataAdapterImpl::AddTextRecord(const std::string& text)
353 {
354     if (data_ != nullptr) {
355         data_->AddTextRecord(text);
356     }
357 }
358 
GetMimeTypes()359 std::vector<std::string> PasteDataAdapterImpl::GetMimeTypes()
360 {
361     return (data_ != nullptr) ? data_->GetMimeTypes() :
362                                 std::vector<std::string>();
363 }
364 
GetPrimaryHtml()365 std::shared_ptr<std::string> PasteDataAdapterImpl::GetPrimaryHtml()
366 {
367     return (data_ != nullptr) ? data_->GetPrimaryHtml() : nullptr;
368 }
369 
GetPrimaryText()370 std::shared_ptr<std::string> PasteDataAdapterImpl::GetPrimaryText()
371 {
372     return (data_ != nullptr) ? data_->GetPrimaryText() : nullptr;
373 }
374 
GetPrimaryMimeType()375 std::shared_ptr<std::string> PasteDataAdapterImpl::GetPrimaryMimeType()
376 {
377     return (data_ != nullptr) ? data_->GetPrimaryMimeType() : nullptr;
378 }
379 
GetRecordAt(std::size_t index)380 std::shared_ptr<PasteDataRecordAdapter> PasteDataAdapterImpl::GetRecordAt(
381     std::size_t index)
382 {
383     if (data_ == nullptr || data_->GetRecordCount() <= index) {
384         return nullptr;
385     }
386     return std::make_shared<PasteDataRecordAdapterImpl>(data_->GetRecordAt(index));
387 }
388 
GetRecordCount()389 std::size_t PasteDataAdapterImpl::GetRecordCount()
390 {
391     return (data_ != nullptr) ? data_->GetRecordCount() : 0;
392 }
393 
AllRecords()394 PasteRecordVector PasteDataAdapterImpl::AllRecords()
395 {
396     if (data_ == nullptr) {
397         return PasteRecordVector();
398     }
399     PasteRecordVector result;
400     for (auto& record: data_->AllRecords()) {
401         result.push_back(std::make_shared<PasteDataRecordAdapterImpl>(record));
402     }
403     return result;
404 }
405 
GetInstance()406 PasteBoardClientAdapterImpl& PasteBoardClientAdapterImpl::GetInstance()
407 {
408     static PasteBoardClientAdapterImpl instance;
409     return instance;
410 }
411 
TransitionCopyOption(CopyOptionMode copyOption)412 MiscServices::ShareOption PasteBoardClientAdapterImpl::TransitionCopyOption(CopyOptionMode copyOption)
413 {
414     auto shareOption = MiscServices::ShareOption::CrossDevice;
415     switch (copyOption) {
416         case CopyOptionMode::IN_APP:
417             shareOption = MiscServices::ShareOption::InApp;
418             break;
419         case CopyOptionMode::LOCAL_DEVICE:
420             shareOption = MiscServices::ShareOption::LocalDevice;
421             break;
422         case CopyOptionMode::CROSS_DEVICE:
423             shareOption = MiscServices::ShareOption::CrossDevice;
424             break;
425         default:
426             break;
427     }
428     return shareOption;
429 }
430 
ReportPasteboardErrorEvent(int32_t errorCode,int32_t recordSize,const std::string & dataType)431 void ReportPasteboardErrorEvent(int32_t errorCode, int32_t recordSize, const std::string &dataType)
432 {
433     OhosAdapterHelper::GetInstance().GetHiSysEventAdapterInstance().Write(PASTE_BOARD_ERROR,
434         HiSysEventAdapter::EventType::FAULT, { ERROR_CODE, std::to_string(errorCode),
435             RECORD_SIZE, std::to_string(recordSize), DATA_TYPE, dataType });
436 }
437 
GetPasteMimeTypeExtention(const PasteRecordVector & data)438 std::string GetPasteMimeTypeExtention(const PasteRecordVector& data)
439 {
440     if (data.empty()) {
441         return MIMETYPE_NULL;
442     }
443     bool isHybrid = false;
444     std::string primaryMimeType = data.front()->GetMimeType();
445     for (auto &item : data) {
446         if (primaryMimeType != item->GetMimeType()) {
447             isHybrid = true;
448             break;
449         }
450     }
451     if (isHybrid) {
452         return MIMETYPE_HYBRID;
453     }
454     return primaryMimeType;
455 }
456 
GetPasteData(PasteRecordVector & data)457 bool PasteBoardClientAdapterImpl::GetPasteData(PasteRecordVector& data)
458 {
459     PasteData pData;
460     if (!PasteboardClient::GetInstance()->HasPasteData() ||
461         PasteboardClient::GetInstance()->GetPasteData(pData) != static_cast<int32_t>(PasteboardError::E_OK)) {
462         WVLOG_E("no data to paste or get data from clipboard failed");
463         ReportPasteboardErrorEvent(PasteboardClient::GetInstance()->GetPasteData(pData),
464             pData.AllRecords().size(), GetPasteMimeTypeExtention(data));
465         isLocalPaste_ = false;
466         tokenId_ = 0;
467         return false;
468     }
469     for (auto& record: pData.AllRecords()) {
470         data.push_back(std::make_shared<PasteDataRecordAdapterImpl>(record));
471     }
472     tokenId_ = pData.GetTokenId();
473     isLocalPaste_ = pData.IsLocalPaste();
474     return true;
475 }
476 
SetPasteData(const PasteRecordVector & data,CopyOptionMode copyOption)477 void PasteBoardClientAdapterImpl::SetPasteData(const PasteRecordVector& data, CopyOptionMode copyOption)
478 {
479     if (copyOption == CopyOptionMode::NONE) {
480         WVLOG_E("SetPasteData failed, copy option mode is 'NONE'");
481         return;
482     }
483     std::vector<std::shared_ptr<PasteDataRecord>> recordList;
484     for (auto& record: data) {
485         PasteDataRecordAdapterImpl* rawRecord =
486             reinterpret_cast<PasteDataRecordAdapterImpl*>(record.get());
487         if (rawRecord == nullptr) {
488             continue;
489         }
490         recordList.push_back(rawRecord->GetRecord());
491     }
492     PasteData pData(recordList);
493     pData.SetTag(webviewPasteDataTag_);
494     auto shareOption = TransitionCopyOption(copyOption);
495     pData.SetShareOption(shareOption);
496     int32_t ret = PasteboardClient::GetInstance()->SetPasteData(pData);
497     if (ret != static_cast<int32_t>(PasteboardError::E_OK)) {
498         WVLOG_E("set paste data to clipboard failed");
499         ReportPasteboardErrorEvent(ret, pData.AllRecords().size(), GetPasteMimeTypeExtention(data));
500     }
501 }
502 
HasPasteData()503 bool PasteBoardClientAdapterImpl::HasPasteData()
504 {
505     return PasteboardClient::GetInstance()->HasPasteData();
506 }
507 
Clear()508 void PasteBoardClientAdapterImpl::Clear()
509 {
510     PasteRecordVector recordVector;
511     if (!GetPasteData(recordVector)) {
512         WVLOG_E("get paste data failed while clear");
513         PasteboardClient::GetInstance()->Clear();
514         return;
515     }
516     for (auto& record: recordVector) {
517         PasteDataRecordAdapterImpl* rawRecord =
518             reinterpret_cast<PasteDataRecordAdapterImpl*>(record.get());
519         if (rawRecord == nullptr) {
520             continue;
521         }
522         rawRecord->Clear();
523     }
524     PasteboardClient::GetInstance()->Clear();
525 }
526 
OpenRemoteUri(const std::string & path)527 int32_t PasteBoardClientAdapterImpl::OpenRemoteUri(const std::string& path)
528 {
529     return RemoteUri::OpenRemoteUri(path);
530 }
531 
IsLocalPaste()532 bool PasteBoardClientAdapterImpl::IsLocalPaste()
533 {
534     return isLocalPaste_;
535 }
536 
GetTokenId()537 uint32_t PasteBoardClientAdapterImpl::GetTokenId()
538 {
539     return tokenId_;
540 }
541 
AddPasteboardChangedObserver(std::shared_ptr<PasteboardObserverAdapter> callback)542 int32_t PasteBoardClientAdapterImpl::AddPasteboardChangedObserver(
543     std::shared_ptr<PasteboardObserverAdapter> callback)
544 {
545     static int32_t count = 0;
546     int32_t id = -1;
547     if (callback) {
548         sptr<PasteboardObserver> observer;
549         {
550             std::lock_guard<std::mutex> lock(mutex_);
551             observer = new (std::nothrow) PasteboardObserverAdapterImpl(callback);
552             if (!observer) {
553                 return -1;
554             }
555 
556             id = count++;
557             if (count < 0) {
558                 count = 0;
559             }
560             reg_.emplace(std::make_pair(id, observer));
561         }
562         PasteboardClient::GetInstance()->AddPasteboardChangedObserver(observer);
563     }
564     return id;
565 }
566 
RemovePasteboardChangedObserver(int32_t callbackId)567 void PasteBoardClientAdapterImpl::RemovePasteboardChangedObserver(
568     int32_t callbackId)
569 {
570     sptr<PasteboardObserver> observer;
571     {
572         std::lock_guard<std::mutex> lock(mutex_);
573         ObserverMap::iterator iter = reg_.find(callbackId);
574         if (iter == reg_.end()) {
575             return;
576         }
577         observer = iter->second;
578         reg_.erase(iter);
579     }
580     PasteboardClient::GetInstance()->RemovePasteboardChangedObserver(observer);
581 }
582 } // namespace OHOS::NWeb
583