clipboard 剪下板js chrom參考
阿新 • • 發佈:2020-07-16
clipboard js api實現
js示例程式碼:
navigator.clipboard.writeText(info).then(function() { /* clipboard successfully set */ showMsg("Success to write clipboard:"+info) }, function() { showMsg("Fail to write clipboard!") /* clipboard write failed */ });
由idl自動生成的繫結程式碼進入:
自動生成的繫結程式碼:v8_clipboard.cc
void V8Clipboard::WriteTextMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_Clipboard_writeText"); ExecutionContext* execution_context_for_measurement = CurrentExecutionContext(info.GetIsolate()); UseCounter::Count(execution_context_for_measurement, WebFeature::kAsyncClipboardAPIWriteText); clipboard_v8_internal::WriteTextMethod(info); } static void WriteTextMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { ExceptionState exception_state(info.GetIsolate(), ExceptionState::kExecutionContext, "Clipboard", "writeText"); ExceptionToRejectPromiseScope reject_promise_scope(info, exception_state); // V8DOMConfiguration::kDoNotCheckHolder// Make sure that info.Holder() really points to an instance of the type. if (!V8Clipboard::HasInstance(info.Holder(), info.GetIsolate())) { exception_state.ThrowTypeError("Illegal invocation"); return; } Clipboard* impl = V8Clipboard::ToImpl(info.Holder()); ScriptState* script_state = ScriptState::ForRelevantRealm(info); if (UNLIKELY(info.Length() < 1)) { exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length())); return; } V8StringResource<> data; data = info[0]; if (!data.Prepare(exception_state)) return; ScriptPromise result = impl->writeText(script_state, data); V8SetReturnValue(info, result.V8Value()); }
- idl third_party\blink\renderer\modules\clipboard\clipboard.idl:
[ SecureContext, Exposed=Window ] interface Clipboard : EventTarget { [MeasureAs=AsyncClipboardAPIRead, CallWith=ScriptState, RuntimeEnabled=AsyncClipboard ] Promise<sequence<ClipboardItem>> read(); [MeasureAs=AsyncClipboardAPIReadText, CallWith=ScriptState ] Promise<DOMString> readText(); [MeasureAs=AsyncClipboardAPIWrite, CallWith=ScriptState, RuntimeEnabled=AsyncClipboard ] Promise<void> write(sequence<ClipboardItem> data); [MeasureAs=AsyncClipboardAPIWriteText, CallWith=ScriptState ] Promise<void> writeText(DOMString data); };
2,cpp實現
clipboard.cc clipboard.h
通過v8 bind,呼叫到。比如writeText()
3,clipboard_promise.cc 實現了js promise
ScriptPromise ClipboardPromise::CreateForWriteText(ScriptState* script_state, const String& data) { ClipboardPromise* clipboard_promise = MakeGarbageCollected<ClipboardPromise>(script_state); clipboard_promise->GetTaskRunner()->PostTask( FROM_HERE, WTF::Bind(&ClipboardPromise::HandleWriteText, WrapPersistent(clipboard_promise), data)); return clipboard_promise->script_promise_resolver_->Promise(); }
void ClipboardPromise::HandleWriteText(const String& data) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); plain_text_ = data; CheckWritePermission(WTF::Bind( &ClipboardPromise::HandleWriteTextWithPermission, WrapPersistent(this))); }
這裡取檢查許可權:
void ClipboardPromise::CheckWritePermission( PermissionService::HasPermissionCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(script_promise_resolver_); if (!IsFocusedDocument(ExecutionContext::From(script_state_))) { script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( DOMExceptionCode::kNotAllowedError, "Document is not focused.")); return; } if (!GetPermissionService()) { script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( DOMExceptionCode::kNotAllowedError, "Permission Service could not connect.")); return; }
void ClipboardPromise::HandleWriteTextWithPermission(PermissionStatus status) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (status != PermissionStatus::GRANTED) { script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( DOMExceptionCode::kNotAllowedError, "Write permission denied.")); return; } SystemClipboard::GetInstance().WritePlainText(plain_text_); SystemClipboard::GetInstance().CommitWrite(); script_promise_resolver_->Resolve(); }
4,最終到達系統的剪下板
third_party\blink\renderer\core\clipboard\system_clipboard.cc
void SystemClipboard::WritePlainText(const String& plain_text, SmartReplaceOption) { // TODO(https://crbug.com/106449): add support for smart replace, which is // currently under-specified. String text = plain_text; #if defined(OS_WIN) ReplaceNewlinesWithWindowsStyleNewlines(text); #endif clipboard_->WriteText(NonNullString(text)); }
其中
clipboard_是個mojo遠端呼叫物件:mojo::Remote<mojom::blink::ClipboardHost> clipboard_;,實現物件介面在
ClipboardHost中。
5,這裡已經跨程序,執行在browser程序中。Host即表示在browser程序中。
content\browser\renderer_host\clipboard_host_impl.cc
void ClipboardHostImpl::WriteText(const base::string16& text) { clipboard_writer_->WriteText(text); }
std::unique_ptr<ui::ScopedClipboardWriter> clipboard_writer_;
6,進入ui\base\clipboard\scoped_clipboard_writer.cc
寫其實是壓棧:
void ScopedClipboardWriter::WriteText(const base::string16& text) { std::string utf8_text = base::UTF16ToUTF8(text); Clipboard::ObjectMapParams parameters; parameters.push_back( Clipboard::ObjectMapParam(utf8_text.begin(), utf8_text.end())); objects_[Clipboard::ObjectType::kText] = parameters; }
然後調commit,真正對呼叫到win/linux x11,系統的剪下板上。
void ClipboardHostImpl::CommitWrite() { clipboard_writer_.reset( new ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)); }
ScopedClipboardWriter::~ScopedClipboardWriter() { if (!objects_.empty()) Clipboard::GetForCurrentThread()->WriteObjects(buffer_, objects_); }
往下進入win移植層:
對於js,promise有 reject,resolve回撥,對應cpp中為:
promise被拒:
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( DOMExceptionCode::kNotAllowedError, "Document is not focused."));
clipboard api參考:
https://www.inovex.de/blog/clipboard-api/
https://w3c.github.io/clipboard-apis/
js:
const copy = document.getElementById('copyme'); copy.addEventListener('click', copyToClipboard); function copyToClipboard() { // set new clipboard data function setData(e) { e.preventDefault(); e.clipboardData.setData('text/plain', 'Hello, world!'); document.removeEventListener('copy', setData); }; document.addEventListener('copy', setData); let result; try { // execute copy command document.execCommand('copy'); result = 'Copied'; } catch (err) { console.log(err); result = 'Unable to copy'; } // show whether copy was successfull document.getElementById('result').innerHTML = result; setTimeout(() => document.getElementById('result').innerHTML = '', 3000); };
<a href="https://w3c.github.io/clipboard-apis/#clipboard-event-api"> <h3>Clipboard Event API (synchronous)</h3> </a> <button id="copyme"> Copy Me! </button> <br/> <div id="result"></div> <br/> <hr/><br/> Try to paste