1. 程式人生 > 實用技巧 >clipboard 剪下板js chrom參考

clipboard 剪下板js chrom參考

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()); }

    1. 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