天天看點

clipboard 剪切闆js chrom參考

clipboard api參考:

https://www.inovex.de/blog/clipboard-api/

https://w3c.github.io/clipboard-apis/

mdn的文檔:https://developer.mozilla.org/en-US/docs/Web/API/Clipboard

js promise詳解:https://www.jb51.net/article/139825.htm

有深度的示例:http://help.dottoro.com/ljwexqxl.php 

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方法:
  SystemClipboard::GetInstance().WritePlainText(plain_text_);
  SystemClipboard::GetInstance().CommitWrite();
這裡回調js裡promise的resolve方法。(失敗的話會調 reject方法)。
  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:

void ClipboardHostImpl::CommitWrite() {
  clipboard_writer_.reset(
      new ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste));
}      
ScopedClipboardWriter::~ScopedClipboardWriter() {
  if (!objects_.empty())
    Clipboard::GetForCurrentThread()->WriteObjects(buffer_, objects_);
}      

往下進入win移植層:clipboard_win.cc

void ClipboardWin::WriteObjects(ClipboardBuffer buffer,
                                const ObjectMap& objects) {
  DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);

  ScopedClipboard clipboard;
  if (!clipboard.Acquire(GetClipboardWindow()))
    return;

  ::EmptyClipboard();

  for (const auto& object : objects)
    DispatchObject(object.first, object.second);
}

void ClipboardWin::WriteText(const char* text_data, size_t text_len) {
  base::string16 text;
  base::UTF8ToUTF16(text_data, text_len, &text);
  HGLOBAL glob = CreateGlobalData(text);

  WriteToClipboard(CF_UNICODETEXT, glob);
}      

對于js,promise有 reject,resolve回調,對應cpp中為:

promise被拒:

script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
        DOMExceptionCode::kNotAllowedError, "Document is not focused."));      

 在clipboard寫資料後,添加回調通知:

{
    ExecutionContext *context=  ExecutionContext::From(script_state_);
    Document* doc = To<Document>(context);
    LocalDOMWindow* executing_window = doc->ExecutingWindow();

      /* call to js */
    Event* ce = Event::CreateBubble(event_type_names::kCopy);
    //(event_interface_names::kCustomEvent);
     // ce->SetType("copy");
      executing_window->DispatchEvent(*ce, NULL);
    
  }      

js代碼:

clipboard 剪切闆js chrom參考
clipboard 剪切闆js chrom參考
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>我的首頁 -</title>
    <script type="application/javascript" src="jquery.js"></script>
<style>
    .designBtn{
        margin-bottom: 20px;
    }
.button{
    background-color: #00a4ff;
    color: #fff;
    display: inline-block;
    border-color: #008aff;
    padding: 6px 3px;
}
    .button:hover{
        background-color: #0074ff;
    }
</style>
</head>
<body>
<div class="designBtn">
    <a href="${response.encodeURL(ctx+'/user/logout')}"  >設計</a>
    <a href="http://www.163.com" class="sheji" onclick="design(event)">設計</a>
</div>

        <input type="text" name="info" id="info">

  <div class="button"  >Copy</div>
<div class="view">

</div>
<script>
   function getclipcontent(){
       navigator.clipboard.readText().then(
                      clipText => {
                          console.log(clipText);
                          showMsg("Success to write clipboard:"+clipText)
                      }
                      );
       
   }
   
     navigator.clipboard.addEventListener('clipboardchange',  function (event) {  
         
             console.log("clipboardchange un impl");       
                 navigator.clipboard.readText().then(
                      clipText => {
                          console.log(clipText);
                      }
                      );;
  
        });
    
       

       navigator.clipboard.addEventListener('copy',  function (event) {  
         
             console.log("clipboard listener copy");    
             
             //self.setTimeout("getclipcontent()",1);//delay
             
              getclipcontent();  
                 ;
  
        });   
                
        addEventListener('copy', (e) => {
        console.log("window listener copy");    
             
             //self.setTimeout("getclipcontent()",1);//delay
             
              getclipcontent();  
});
        
        document.addEventListener('copy', (e) => {
            console.log("document copy listener");  
  e.preventDefault();
  e.clipboardData.setData('text/plain', 'Hello World');
});

        
    var clip={
        action:0,//0write,1 paste
        content:null,//message
    }

    function handlePermission() {
        navigator.permissions.query({name:'clipboard-write'}).then(replyPermission);
        navigator.permissions.query({name:'clipboard-read'}).then(replyPermission);
    }

    function replyPermission(result){
        showMsg("permission:"+result.state);
        if (result.state == 'granted') {
          //  report(result.state);
           // geoBtn.style.display = 'none';
        } else if (result.state == 'prompt') {

         //   report(result.state);
          //  navigator.geolocation.getCurrentPosition(revealPosition,positionDenied,geoSettings);

        } else if (result.state == 'denied') {
        //    report(result.state);
       //     geoBtn.style.display = 'inline';
            showMsg("You've prevented from copy or paste!")
        }
        result.onchange = function() {
            showMsg("permission:"+result.state);
        }

    }
$(".button").bind('click',function(e){
    var info=$("#info").val();
    navigator.clipboard.writeText(info).then(function() {
        navigator.clipboard.readText().then(
                      clipText => {
                          /* clipboard successfully set */
                    showMsg("Success to write clipboard:"+clipText)
                      }
                      );
        
    }, function() {
        showMsg("Fail to write clipboard!")
        /* clipboard write failed */
    });
    $(".view").append("click!")

})
    function showMsg(msg){
        $(".view").append(msg+"<br>");
    }
    handlePermission();
    
    

</script>

</body>
</html>      

View Code

代碼2:

clipboard 剪切闆js chrom參考
clipboard 剪切闆js chrom參考
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
    <!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
    
  </head>
  <body >
 
<input type="text" id="contents" />
<input type="button" onClick="copy0()" value="複制" />  
<input type="button" onClick="setClipboard()" value="複制剪切闆" />  

<script type="text/javascript">   
function copy0(){   
    var e=document.getElementById("contents");//對象是contents   
    e.select(); //選擇對象   
    nice=document.execCommand("Copy"); //執行浏覽器複制指令  
    if(nice){  
        alert('複制内容成功');  
    }  
}

function setClipboard() {
      
  let data = new DataTransfer();

  data.items.add("text/plain", "abcd");
  navigator.clipboard.write(data).then(function() {
    /* success */
    console.log("copy success.")
  }, function() {
    /* failure */
    console.log("copy failure.")
  });
}

function oncopy1(){
    
    navigator.clipboard.readText().then(
  clipText => {
      //document.querySelector(".cliptext0").innerText = "aaaaaaaaaaaa";
      console.log(clipText);
      }
  );;
}

        addEventListener('copy', function (event) {         
                 navigator.clipboard.readText().then(
                      clipText => {
                          console.log(clipText);
                      }
                      );;
  
        });

</script> 
      
      </body>
</html>      

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 😉 <br/>
<textarea rows="4" cols="40"></textarea>