Skip to content

Commit

Permalink
Merge pull request #99 from che-incubator/rebase-projector-sources
Browse files Browse the repository at this point in the history
Rebase against the upstream Projector sources
  • Loading branch information
vzhukovs authored Apr 11, 2022
2 parents eaa2a4b + 988d38b commit 61d0004
Show file tree
Hide file tree
Showing 56 changed files with 521 additions and 256 deletions.
6 changes: 6 additions & 0 deletions projector-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin

plugins {
kotlin("multiplatform") apply false
Expand Down Expand Up @@ -61,6 +63,10 @@ subprojects {
}
}

plugins.withType(NodeJsRootPlugin::class.java) {
the<NodeJsRootExtension>().nodeVersion = "16.14.2"
}

if (System.getenv("CHROME_BIN") == null) {
gradle.taskGraph.beforeTask {
if (name in setOf("jsTest", "jsBrowserTest", "browserTest")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Unfortunately, we can't just continuously get clipboard data from [`window.navig

It's vice versa: when your clipboard is changed on the server side, the client needs to apply the change on its side.

We set the clipboard on the client side via [`window.navigator.clipboard`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard). **This doesn't work in [insecure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts/features_restricted_to_secure_contexts), so the client needs to be opened using HTTPS or on localhost to support this**.
We set the clipboard on the client side via [`window.navigator.clipboard`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/clipboard) when client is opened using HTTPS or on localhost. If client is opened in insecure context or doesn't support Async Clipboard API we fall back to `document.execCommand("copy")`. If it also didn't work out then we show prompt so that you can manually copy new contents of server clipboard.

We can't use ["copy" listener](https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event) because when this event is generated, we don't have a message from the server with actual clipboard data yet. Also, this method won't work if you click a "copy" button in your application.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ Name | Type | Default value

Setting this option to `false` will enable the platform and plugins updates.

### Disabling attaching to the IDE

Name | Type | Default value
---|---|---
`ORG_JETBRAINS_PROJECTOR_SERVER_ATTACH_TO_IDE` | Boolean | `true`

Setting this option to `false` will disable integrations of Projector with the IDE. By default (`true`), Projector decides it automatically: it attaches if it's able to do so.

## Difference between env vars and system props

Expand Down
2 changes: 1 addition & 1 deletion projector-client/docSrc/src/doc/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extra:
- icon: fontawesome/brands/github
# TODO: rename to "projector"?
link: https://github.com/JetBrains/projector-client
- icon: fontawesome/brands/telegram-plane
- icon: fontawesome/brands/telegram
link: https://t.me/JBProjector
- icon: fontawesome/brands/twitter
link: https://twitter.com/ProjectorJB
Expand Down
2 changes: 1 addition & 1 deletion projector-client/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ coroutinesVersion=1.6.0
dnsjavaVersion=3.4.3
electronVersion=^16.0.6
electronPackagerVersion=^15.1.0
gradleMkdocsPluginVersion=2.2.0
gradleMkdocsPluginVersion=2.3.0
intellijPlatformVersion=213.6461.23
intellijMarkdownPluginVersion=213.5744.223
intellijJcefVersion=89.0.12-g2b76680-chromium-89.0.4389.90-api-1.6
Expand Down
2 changes: 2 additions & 0 deletions projector-client/projector-agent-ij-injector/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ tasks.withType<Jar> {
)
}

duplicatesStrategy = DuplicatesStrategy.WARN

exclude("META-INF/versions/9/module-info.class")

from(inline(configurations.runtimeClasspath))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,10 @@ class Renderer(private val renderingSurface: RenderingSurface) {
requestedState.paint = color.toColor()
}

fun setGradientPaint(p1: Point, p2: Point, color1: Int, color2: Int) {
val linearGradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y).apply {
addColorStop(0.0, color1)
addColorStop(1.0, color2)
fun setGradientPaint(p1: Point, p2: Point, fractions: List<Double>, argbs: List<Int>) {
val linearGradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y)
fractions.zip(argbs).forEach { (fraction, argb) ->
linearGradient.addColorStop(fraction, argb)
}

requestedState.paint = linearGradient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ class SingleRenderingSurfaceProcessor(private val renderingSurface: RenderingSur
is PaintValue.Gradient -> renderer.setGradientPaint(
p1 = paintValue.p1,
p2 = paintValue.p2,
color1 = paintValue.argb1,
color2 = paintValue.argb2
fractions = paintValue.fractions,
argbs = paintValue.argbs,
)

is PaintValue.Unknown -> logUnsupportedCommand(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ actual object ParamsProvider {
private const val DEFAULT_REPAINT_INTERVAL_MS = 333
private const val DEFAULT_IMAGE_CACHE_SIZE_CHARS = 5_000_000
private const val DEFAULT_BLOCK_CLOSING = true
private const val DEFAULT_SPECULATIVE_TYPING_LATENCY = 0

val SYSTEM_SCALING_RATIO
get() = window.devicePixelRatio // get every time because it can be changed
Expand Down Expand Up @@ -101,6 +102,7 @@ actual object ParamsProvider {
actual val IMAGE_CACHE_SIZE_CHARS: Int
val BLOCK_CLOSING: Boolean
val LAYOUT_TYPE: LayoutType
val SPECULATIVE_TYPING_LATENCY: Int
val SCALING_RATIO: Double
get() = SYSTEM_SCALING_RATIO * USER_SCALING_RATIO

Expand Down Expand Up @@ -162,6 +164,7 @@ actual object ParamsProvider {
"frAzerty" -> LayoutType.FR_AZERTY
else -> LayoutType.JS_DEFAULT
}
SPECULATIVE_TYPING_LATENCY = searchParams.get("speculativeTypingLatency")?.toIntOrNull() ?: DEFAULT_SPECULATIVE_TYPING_LATENCY
}
}

Expand Down
1 change: 1 addition & 0 deletions projector-client/projector-client-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Name | Type | Default value | Description
`cacheSize` | Int | `5M` | Set size of cache for images in Chars.
`blockClosing` | Boolean | `true` | Enable blocking of accidental closing of the web page
`relayServerId` | String? | Not present | Identifier of Projector server to connect to for relay connection. Warning: Static files must be accessed via https when relay is used.
`speculativeTypingLatency` | Int | `0` | Sets latency before key press event is sent to server if speculative symbol for the event was drawn.

## Shortcuts
- `Ctrl + F10` prints statistics to the browser console. Example:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (c) 2019-2021 JetBrains s.r.o.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.jetbrains.projector.client.web

import kotlinx.browser.document
import kotlinx.browser.window
import org.jetbrains.projector.client.web.misc.isDefined
import org.jetbrains.projector.client.web.misc.isElectron
import org.jetbrains.projector.client.web.misc.isGecko
import org.jetbrains.projector.common.protocol.toServer.ClientNotificationEvent
import org.jetbrains.projector.common.protocol.toServer.ClientNotificationType
import org.jetbrains.projector.util.logging.Logger
import org.w3c.dom.HTMLTextAreaElement

/**
* First try to copy via `window.navigator.clipboard`. If this method fails or is not available,
* then try to copy via `document.execCommand` (deprecated, but still supported by firefox).
* If this also fails, then fallback to asking user to copy manually.
*/
class ClipboardHandler(private val onCopyFailed: (ClientNotificationEvent) -> Unit) {

private val logger = Logger<ClipboardHandler>()

fun copyText(text: String) {
if (isDefined(window.navigator.clipboard)) {
window.navigator.clipboard.writeText(text)
.catch {
logger.error(it) { "Error writing clipboard: $it" }
fallbackCopy(text, it.message ?: "Unknown error")
}
}
else {
fallbackCopy(text, "Clipboard API is not available")
}
}

private fun fallbackCopy(textToCopy: String, reason: String) {
val execCommandReason = tryCopyTextViaExecCommand(textToCopy)
if (execCommandReason != null) {
askUserToCopyClipboardManually(textToCopy, reason, execCommandReason)
}
}

/**
* Tries to automatically copy text to clipboard via Document.execCommand
*
* @return null if copy successful, string describing fail reason otherwise
*/
private fun tryCopyTextViaExecCommand(textToCopy: String): String? {

when {
!isDefined(js("document.execCommand")) -> return "Document.execCommand is not available"

// Chrome only allows Document.execCommand from event callbacks
!isGecko() -> return "Copying using Document.execCommand works only in Firefox"
}

val textArea = document.createElement("textarea") as HTMLTextAreaElement
textArea.id = "copyArea"
textArea.value = textToCopy
textArea.style.display = "hidden"

document.body!!.appendChild(textArea)

textArea.focus()
textArea.select()

var isCopied = false
try {
isCopied = document.execCommand("copy")
}
catch (t: Throwable) { // May throw SecurityError https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#the-copy-command
logger.error(t) { "Error while running 'document.execCommand(\"copy\")'" }
}
finally {
textArea.remove()
}

return if (isCopied) null else "Document.execCommand failed, check console for info from browser"
}

private fun askUserToCopyClipboardManually(textToCopy: String, vararg reasons: String) {
val message = "A clipboard change on the server detected but can't synchronize your clipboard with it automatically " +
"(reasons: ${reasons.joinToString("; ")}). Please copy text on next line manually:"

if (isElectron()) { // TODO window.prompt is not available in electron
val title = "Copying failed"
val notificationMessage = "Copying is not currently supported in insecure context in Projector launcher"
val type = ClientNotificationType.ERROR
onCopyFailed(ClientNotificationEvent(title, notificationMessage, type))
}
else {
window.prompt(message, textToCopy)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,29 @@
*/
package org.jetbrains.projector.client.web

import kotlinx.browser.window
import org.jetbrains.projector.client.common.RenderingQueue
import org.jetbrains.projector.client.web.component.MarkdownPanelManager
import org.jetbrains.projector.client.web.input.InputController
import org.jetbrains.projector.client.web.misc.PingStatistics
import org.jetbrains.projector.client.web.misc.*
import org.jetbrains.projector.client.web.speculative.Typing
import org.jetbrains.projector.client.web.state.ClientAction
import org.jetbrains.projector.client.web.state.ClientStateMachine
import org.jetbrains.projector.client.web.state.ProjectorUI
import org.jetbrains.projector.client.web.window.OnScreenMessenger
import org.jetbrains.projector.client.web.window.WindowDataEventsProcessor
import org.jetbrains.projector.client.web.window.WebWindowManager
import org.jetbrains.projector.client.web.window.WindowDataEventsProcessor
import org.jetbrains.projector.common.misc.Do
import org.jetbrains.projector.common.protocol.toClient.*
import org.jetbrains.projector.util.logging.Logger

class ServerEventsProcessor(
private val windowManager: WebWindowManager,
private val windowDataEventsProcessor: WindowDataEventsProcessor,
private val renderingQueue: RenderingQueue,
private val stateMachine: ClientStateMachine,
) {

private val clipboardHandler = ClipboardHandler { stateMachine.fire(ClientAction.AddEvent(it)) }

fun process(
commands: ToClientMessageType, pingStatistics: PingStatistics, typing: Typing, markdownPanelManager: MarkdownPanelManager,
inputController: InputController,
Expand All @@ -68,7 +71,7 @@ class ServerEventsProcessor(
inputController.handleCaretInfoChange(command.data)
}

is ServerClipboardEvent -> handleServerClipboardChange(command)
is ServerClipboardEvent -> clipboardHandler.copyText(command.stringContent)

is ServerPingReplyEvent -> pingStatistics.onPingReply(command)

Expand Down Expand Up @@ -111,13 +114,4 @@ class ServerEventsProcessor(
windowDataEventsProcessor.onResized()
}

private fun handleServerClipboardChange(event: ServerClipboardEvent) {
window.navigator.clipboard.writeText(event.stringContent)
.catch { logger.error { "Error writing clipboard: $it" } }
}

companion object {

private val logger = Logger<ServerEventsProcessor>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
package org.jetbrains.projector.client.web

import kotlinx.browser.window
import org.jetbrains.projector.client.web.electron.isElectron
import org.jetbrains.projector.client.web.misc.isElectron
import org.w3c.dom.url.URL

object UriHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,10 @@ class WindowSizeController(private val stateMachine: ClientStateMachine) {
stateMachine.fire(ClientAction.WindowResize)
}

fun addListener() {
init {
window.addEventListener(RESIZE_EVENT_TYPE, ::handleResizeEvent)
}

fun removeListener() {
window.removeEventListener(RESIZE_EVENT_TYPE, ::handleResizeEvent)
}

companion object {

private const val RESIZE_EVENT_TYPE = "resize"
Expand Down
Loading

0 comments on commit 61d0004

Please sign in to comment.