Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Update server clipboard on tab activation #143

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docSrc/src/doc/docs/ij_user_guide/accessing.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ There are some limitations with clipboard.

When your clipboard is changed on the client side, the server needs to apply the change on its side.

We implement it on the client side via setting ["paste" listener](https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event). So clipboard is updated on the server only if you invoke that listener, for example, by hitting Ctrl+V or Ctrl+Shift+V. **If you have an application on the server side with a "paste" button, a click on it can paste outdated information unless the listener wasn't invoked**.
We implement it on the client side by reading clipboard contents when the browser tab is activated, then sending to the server.
This allows a pretty seamless user experience.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we still don't support a scenario when another app changes clipboard without activation of our browser tab. This case is rare (probably some special software) but I think we could mention that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the situation when clipboard is changed in background while user uses projected IDE in active browser tab?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I assume the silence means yes

This approach is tested in Firefox (requires `dom.events.testing.asyncClipboard` flag enabled in`about:config`) and Chromium based browsers.
Safari is not supported due to restrictions on clipboard access.
Also site requires secure context support.

Fallback implementation uses ["paste" listener](https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event). So clipboard is updated on the server only if you invoke that listener, for example, by hitting Ctrl+V or Ctrl+Shift+V. **If you have an application on the server side with a "paste" button, a click on it can paste outdated information unless the listener wasn't invoked**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really "fallback"? Looks like it's still always invoked when we paste, so we just update the clipboard without need. It can be improved, let's add todo in code or comment in documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without need

It is still needed in case we copy on the same page


Unfortunately, we can't just continuously get clipboard data from [`window.navigator.clipboard`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard) and send it to the server because when it's invoked not from user's context, there will be alert from the browser like "the site wants to read clipboard info, do you grant?".

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,28 @@ class ClipboardHandler(private val onCopyFailed: (ClientNotificationEvent) -> Un
}
}

companion object {

private var lastClipboardString: String? = null

/**
* Attempts to get clipboard contents, then passes it to the callback function if it's a new value.
* Restrictions:
* - Requires secure context
* - In firefox function `window.navigator.clipboard.readText` requires flag `dom.events.testing.asyncClipboard` in
* `about:config` to be enabled
* - Safari can only read clipboard from handlers of mouse press events and similar
*/
fun getNewClipboardString(callback: (String) -> Unit) {
if (isDefined(window.navigator.clipboard) && isDefined(js("window.navigator.clipboard.readText"))) {
window.navigator.clipboard.readText().then {
if (it != lastClipboardString) {
lastClipboardString = it
callback(it)
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ package org.jetbrains.projector.client.web.window
import kotlinx.browser.window
import org.jetbrains.projector.client.common.misc.ImageCacher
import org.jetbrains.projector.client.common.window.WindowManager
import org.jetbrains.projector.client.web.ClipboardHandler
import org.jetbrains.projector.client.web.state.ClientAction
import org.jetbrains.projector.client.web.state.ClientStateMachine
import org.jetbrains.projector.client.web.state.LafListener
import org.jetbrains.projector.common.protocol.toClient.WindowData
import org.jetbrains.projector.common.protocol.toServer.ClientClipboardEvent
import org.jetbrains.projector.common.protocol.toServer.ClientWindowsActivationEvent
import org.jetbrains.projector.common.protocol.toServer.ClientWindowsDeactivationEvent
import org.w3c.dom.HTMLCanvasElement
Expand All @@ -52,6 +54,10 @@ class WebWindowManager(private val stateMachine: ClientStateMachine, override va
private fun onActivated(@Suppress("UNUSED_PARAMETER") event: FocusEvent) {
val windowIds = visibleWindows.map { it.id }
stateMachine.fire(ClientAction.AddEvent(ClientWindowsActivationEvent(windowIds)))

ClipboardHandler.getNewClipboardString {
stateMachine.fire(ClientAction.AddEvent(ClientClipboardEvent(it)))
}
}

// todo: remove SUPPRESS after KT-8112 is implemented or KTIJ-15401 is solved in some other way
Expand Down