@@ -8,6 +8,7 @@ import java.awt.event.MouseEvent
88import javax.swing.JPanel
99import javax.swing.SwingUtilities
1010import javax.swing.Timer
11+ import kotlin.concurrent.thread
1112
1213
1314class WryWebViewPanel (
@@ -24,6 +25,8 @@ class WryWebViewPanel(
2425 private var pendingHeaders: Map <String , String > = emptyMap()
2526 private var pendingHtml: String? = null
2627 private var createTimer: Timer ? = null
28+ private var destroyTimer: Timer ? = null
29+ private var createInFlight: Boolean = false
2730 private var gtkTimer: Timer ? = null
2831 private var windowsTimer: Timer ? = null
2932 private var skikoInitialized: Boolean = false
@@ -45,14 +48,15 @@ class WryWebViewPanel(
4548
4649 override fun addNotify () {
4750 super .addNotify()
51+ stopDestroyTimer()
4852 log(" addNotify displayable=${host.isDisplayable} showing=${host.isShowing} size=${host.width} x${host.height} " )
4953 SwingUtilities .invokeLater { scheduleCreateIfNeeded() }
5054 }
5155
5256 override fun removeNotify () {
5357 log(" removeNotify" )
5458 stopCreateTimer()
55- destroyIfNeeded ()
59+ scheduleDestroyIfNeeded ()
5660 super .removeNotify()
5761 }
5862
@@ -282,6 +286,7 @@ class WryWebViewPanel(
282286
283287 private fun createIfNeeded (): Boolean {
284288 if (webviewId != null ) return true
289+ if (createInFlight) return false
285290 if (! host.isDisplayable || ! host.isShowing) return false
286291 if (host.width <= 0 || host.height <= 0 ) return false
287292 // On Windows, wait for the window to be fully visible
@@ -306,48 +311,69 @@ class WryWebViewPanel(
306311 parentHandle = resolved.handle
307312 parentIsWindow = resolved.isWindow
308313 log(" createIfNeeded handle=$parentHandle parentIsWindow=$parentIsWindow size=${host.width} x${host.height} " )
309- return try {
310- val width = host.width.coerceAtLeast(1 )
311- val height = host.height.coerceAtLeast(1 )
312- val userAgent = customUserAgent
313-
314- webviewId =
314+ val width = host.width.coerceAtLeast(1 )
315+ val height = host.height.coerceAtLeast(1 )
316+ val userAgent = customUserAgent
317+ val initialUrl = pendingUrl
318+ val handleSnapshot = parentHandle
319+ createInFlight = true
320+ stopCreateTimer()
321+ thread(name = " wry-webview-create" , isDaemon = true ) {
322+ val createdId = try {
315323 if (userAgent == null ) {
316- NativeBindings .createWebview(parentHandle , width, height, pendingUrl )
324+ NativeBindings .createWebview(handleSnapshot , width, height, initialUrl )
317325 } else {
318- NativeBindings .createWebviewWithUserAgent(parentHandle, width, height, pendingUrl, userAgent)
326+ NativeBindings .createWebviewWithUserAgent(handleSnapshot, width, height, initialUrl, userAgent)
327+ }
328+ } catch (e: RuntimeException ) {
329+ System .err.println (" Failed to create Wry webview: ${e.message} " )
330+ e.printStackTrace()
331+ null
332+ }
333+ SwingUtilities .invokeLater {
334+ createInFlight = false
335+ if (createdId == null ) {
336+ scheduleCreateIfNeeded()
337+ return @invokeLater
338+ }
339+ if (webviewId != null ) {
340+ NativeBindings .destroyWebview(createdId)
341+ return @invokeLater
342+ }
343+ if (! host.isDisplayable || ! host.isShowing) {
344+ NativeBindings .destroyWebview(createdId)
345+ return @invokeLater
319346 }
320- updateBounds()
321- startGtkPumpIfNeeded()
322- startWindowsPumpIfNeeded()
323- // Apply any pending content that requires an explicit call after creation.
324- val id = webviewId
325- val html = pendingHtml
326- val urlWithHeaders = pendingUrlWithHeaders
327- val headers = pendingHeaders
328- if (id != null ) {
347+ webviewId = createdId
348+ updateBounds()
349+ startGtkPumpIfNeeded()
350+ startWindowsPumpIfNeeded()
351+ // Apply any pending content that requires an explicit call after creation.
352+ val html = pendingHtml
353+ val urlWithHeaders = pendingUrlWithHeaders
354+ val headers = pendingHeaders
329355 when {
330356 html != null -> {
331357 pendingHtml = null
332- NativeBindings .loadHtml(id , html)
358+ NativeBindings .loadHtml(createdId , html)
333359 }
334360 urlWithHeaders != null && headers.isNotEmpty() -> {
335361 pendingUrlWithHeaders = null
336362 pendingHeaders = emptyMap()
337- NativeBindings .loadUrlWithHeaders(id, urlWithHeaders, headers)
363+ NativeBindings .loadUrlWithHeaders(createdId, urlWithHeaders, headers)
364+ }
365+ pendingUrl != initialUrl -> {
366+ NativeBindings .loadUrl(createdId, pendingUrl)
338367 }
339368 }
369+ log(" createIfNeeded success id=$webviewId " )
340370 }
341- log(" createIfNeeded success id=$webviewId " )
342- true
343- } catch (e: RuntimeException ) {
344- System .err.println (" Failed to create Wry webview: ${e.message} " )
345- e.printStackTrace()
346- true
347371 }
372+ return true
348373 }
349374
350375 private fun destroyIfNeeded () {
376+ stopDestroyTimer()
351377 stopGtkPump()
352378 stopWindowsPump()
353379 stopBoundsTimer()
@@ -364,7 +390,7 @@ class WryWebViewPanel(
364390 private fun updateBounds () {
365391 val id = webviewId ? : return
366392 val bounds = boundsInParent()
367- if (IS_LINUX ) {
393+ if (IS_LINUX || IS_MAC ) {
368394 pendingBounds = bounds
369395 if (boundsTimer == null ) {
370396 boundsTimer = Timer (16 ) {
@@ -411,7 +437,7 @@ class WryWebViewPanel(
411437 }
412438
413439 private fun scheduleCreateIfNeeded () {
414- if (webviewId != null || createTimer != null ) return
440+ if (webviewId != null || createTimer != null || createInFlight ) return
415441 log(" scheduleCreateIfNeeded" )
416442 val delay = if (IS_WINDOWS ) 100 else 16
417443 createTimer = Timer (delay) {
@@ -426,6 +452,25 @@ class WryWebViewPanel(
426452 createTimer = null
427453 }
428454
455+ private fun scheduleDestroyIfNeeded () {
456+ if (destroyTimer != null ) return
457+ if (webviewId == null && ! createInFlight) return
458+ destroyTimer = Timer (400 ) {
459+ stopDestroyTimer()
460+ if (! host.isDisplayable || ! host.isShowing) {
461+ destroyIfNeeded()
462+ }
463+ }.apply {
464+ isRepeats = false
465+ start()
466+ }
467+ }
468+
469+ private fun stopDestroyTimer () {
470+ destroyTimer?.stop()
471+ destroyTimer = null
472+ }
473+
429474 private fun stopBoundsTimer () {
430475 boundsTimer?.stop()
431476 boundsTimer = null
@@ -442,7 +487,9 @@ class WryWebViewPanel(
442487 }
443488
444489 private fun log (message : String ) {
445- System .err.println (" [WryWebViewPanel] $message " )
490+ if (LOG_ENABLED ) {
491+ System .err.println (" [WryWebViewPanel] $message " )
492+ }
446493 }
447494
448495 private fun resolveParentHandle (): ParentHandle ? {
@@ -531,6 +578,17 @@ class WryWebViewPanel(
531578 private val IS_LINUX = OS_NAME .contains(" linux" )
532579 private val IS_MAC = OS_NAME .contains(" mac" )
533580 private val IS_WINDOWS = OS_NAME .contains(" windows" )
581+ private val LOG_ENABLED = run {
582+ val raw = System .getProperty(" composewebview.wry.log" ) ? : System .getenv(" WRYWEBVIEW_LOG" )
583+ when {
584+ raw == null -> false
585+ raw == " 1" -> true
586+ raw.equals(" true" , ignoreCase = true ) -> true
587+ raw.equals(" yes" , ignoreCase = true ) -> true
588+ raw.equals(" debug" , ignoreCase = true ) -> true
589+ else -> false
590+ }
591+ }
534592 }
535593}
536594
0 commit comments