List icon 目录

弹出窗口

本页面描述了如何处理、显示或抑制弹出窗口。

概述

任何网页都可以使用以下方式之一显示弹出窗口:

  1. 使用 window.open() JavaScript 函数。例如:

    window.open("https://www.google.com", "_blank", "resizable=yes,
        top=500, left=500, width=400, height=400");
    
  2. 通过带有 target 属性的链接:

    <a href="https://www.google.com" target="_blank">打开 Google</a>
    

默认情况下,所有弹出窗口都会被抑制。

打开弹出窗口

为了更改默认行为并控制弹出窗口的创建,请使用 CreatePopupCallbackOpenPopupCallback 回调。

当 Engine 想知道是否可以创建弹出窗口时,将调用 CreatePopupCallback 回调。以下代码显示了如何允许创建弹出窗口:

Java
Kotlin

browser.set(CreatePopupCallback.class, (params) -> CreatePopupCallback.Response.create());

browser.register(CreatePopupCallback {
    CreatePopupCallback.Response.create()
})

如果 CreatePopupCallback 回调允许创建弹出窗口,则会调用 OpenPopupCallback 回调。在此回调中,您可以访问已创建的弹出窗口,并在必要时显示它。例如:

Java
Kotlin

browser.set(OpenPopupCallback.class, (params) -> {
    // 访问已创建的弹出窗口。
    var popup = params.popupBrowser();
    return OpenPopupCallback.Response.proceed();
});

browser.register(OpenPopupCallback { params ->
    // 访问已创建的弹出窗口。
    val popup = params.popupBrowser()
    OpenPopupCallback.Response.proceed()
})

在上述示例中,popup 实例在回调被调用时并没有加载任何网页。网页将在稍后加载。此时,您可以注册所有必需的事件监听器和回调,但不能访问 DOM 或 JavaScript,因为 popup 还没有 Frame

抑制弹出窗口

要抑制弹出窗口,请使用以下方法:

Java
Kotlin

browser.set(CreatePopupCallback.class, params -> CreatePopupCallback.Response.suppress());

browser.register(CreatePopupCallback {
    CreatePopupCallback.Response.suppress()
})

显示弹出窗口

当您创建一个 Swing 或 JavaFX 的 BrowserView 时,它会自动为给定的 Browser 实例配置 CreatePopupCallbackOpenPopupCallback 回调的默认实现。

CreatePopupCallback 的默认实现允许创建所有弹出窗口:

Java
Kotlin

browser.set(CreatePopupCallback.class, params -> CreatePopupCallback.Response.create());

browser.register(CreatePopupCallback {
    CreatePopupCallback.Response.create()
})

Swing 和 JavaFX 中 OpenPopupCallback 回调的默认实现如下所示。

Swing

Swing BrowserView 的默认实现如下:

Java
Kotlin


import com.teamdev.jxbrowser.browser.callback.OpenPopupCallback;
import com.teamdev.jxbrowser.browser.event.BrowserClosed;
import com.teamdev.jxbrowser.browser.event.TitleChanged;
import com.teamdev.jxbrowser.browser.event.UpdateBoundsRequested;
import com.teamdev.jxbrowser.ui.Rect;
import com.teamdev.jxbrowser.ui.Size;
import com.teamdev.jxbrowser.view.swing.BrowserView;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;

import static javax.swing.SwingUtilities.invokeLater;

/**
 * Swing UI 工具包的默认 [OpenPopupCallback] 实现
 * 创建并显示带有嵌入式弹出 Browser 的新窗口。
 */
public final class DefaultOpenPopupCallbackSwing implements OpenPopupCallback {

    private static final int DEFAULT_POPUP_WIDTH = 800;
    private static final int DEFAULT_POPUP_HEIGHT = 600;

    @Override
    public Response on(Params params) {
        var browser = params.popupBrowser();
        invokeLater(() -> {
            var view = BrowserView.newInstance(browser);
            var frame = new JFrame();
            frame.add(view, BorderLayout.CENTER);
            frame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    invokeLater(browser::close);
                }
            });

            updateBounds(frame, params.initialBounds());

            browser.on(TitleChanged.class, event -> invokeLater(() ->
                frame.setTitle(event.title())
            ));
            browser.on(BrowserClosed.class, event -> invokeLater(() -> {
                frame.setVisible(false);
                frame.dispose();
            }));
            browser.on(UpdateBoundsRequested.class, event -> invokeLater(() ->
                updateBounds(frame, event.bounds())
            ));

            frame.setVisible(true);
        });
        return Response.proceed();
    }

    private static void updateBounds(JFrame frame, Rect bounds) {
        if (bounds.size().isEmpty()) {
            frame.setLocationByPlatform(true);
            frame.setSize(DEFAULT_POPUP_WIDTH, DEFAULT_POPUP_HEIGHT);
        } else {
            frame.setLocation(toPoint(bounds.origin()));
            frame.getContentPane().setPreferredSize(toDimension(bounds.size()));
            frame.pack();
        }
    }

    private static Dimension toDimension(Size size) {
        return new Dimension(size.width(), size.height());
    }

    private static Point toPoint(com.teamdev.jxbrowser.ui.Point point) {
        return new Point(point.x(), point.y());
    }
}

import com.teamdev.jxbrowser.browser.callback.OpenPopupCallback
import com.teamdev.jxbrowser.browser.event.BrowserClosed
import com.teamdev.jxbrowser.browser.event.TitleChanged
import com.teamdev.jxbrowser.browser.event.UpdateBoundsRequested
import com.teamdev.jxbrowser.dsl.subscribe
import com.teamdev.jxbrowser.ui.Point
import com.teamdev.jxbrowser.ui.Rect
import com.teamdev.jxbrowser.ui.Size
import com.teamdev.jxbrowser.view.swing.BrowserView
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.swing.JFrame
import javax.swing.SwingUtilities.invokeLater

/**
 * Swing UI 工具包的默认 {@link OpenPopupCallback} 实现
 * 创建并显示带有嵌入式弹出 Browser 的新窗口。
 */
class DefaultOpenPopupCallbackSwing : OpenPopupCallback {

    override fun on(params: OpenPopupCallback.Params): OpenPopupCallback.Response {
        val browser = params.popupBrowser()
        invokeLater {
            val view = BrowserView.newInstance(browser)
            val frame = JFrame()
            frame.add(view, BorderLayout.CENTER)
            frame.addWindowListener(object: WindowAdapter() {
                override fun windowClosing(e: WindowEvent) {
                    invokeLater { browser.close() }
                }
            })
            updateBounds(frame, params.initialBounds())
            browser.subscribe<TitleChanged> { event ->
                invokeLater { frame.title = event.title() }
            }
            browser.subscribe<BrowserClosed> {
                invokeLater {
                    frame.isVisible = false
                    frame.dispose()
                }
            }
            browser.subscribe<UpdateBoundsRequested> { event ->
                invokeLater { updateBounds(frame, event.bounds()) }
            }
            frame.isVisible = true
        }
        return OpenPopupCallback.Response.proceed()
    }

    companion object {
        private const val DEFAULT_POPUP_WIDTH = 800
        private const val DEFAULT_POPUP_HEIGHT = 600
        private fun updateBounds(frame: JFrame, bounds: Rect) {
            if (bounds.size().isEmpty) {
                frame.isLocationByPlatform = true
                frame.setSize(DEFAULT_POPUP_WIDTH, DEFAULT_POPUP_HEIGHT)
            } else {
                frame.location = toPoint(bounds.origin())
                frame.contentPane.preferredSize = toDimension(bounds.size())
                frame.pack()
            }
        }

        private fun toDimension(size: Size): Dimension {
            return Dimension(size.width(), size.height())
        }

        private fun toPoint(point: Point): java.awt.Point {
            return java.awt.Point(point.x(), point.y())
        }
    }
}

JavaFX

JavaFX BrowserView 的默认实现如下:

Java
Kotlin


import com.teamdev.jxbrowser.browser.callback.OpenPopupCallback;
import com.teamdev.jxbrowser.browser.event.BrowserClosed;
import com.teamdev.jxbrowser.browser.event.TitleChanged;
import com.teamdev.jxbrowser.browser.event.UpdateBoundsRequested;
import com.teamdev.jxbrowser.ui.Rect;
import com.teamdev.jxbrowser.view.javafx.BrowserView;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import static javafx.application.Platform.runLater;

/**
 * JavaFX UI 工具包的默认 {@link OpenPopupCallback} 实现
 * 创建并显示带有嵌入式弹出 Browser 的新窗口。
 */
public final class DefaultOpenPopupCallbackJavaFx implements OpenPopupCallback {

    private static final int DEFAULT_POPUP_WIDTH = 800;
    private static final int DEFAULT_POPUP_HEIGHT = 600;

    @Override
    public Response on(Params params) {
        var browser = params.popupBrowser();
        runLater(() -> {
            var view = BrowserView.newInstance(browser);
            var stage = new Stage();
            var root = new StackPane();
            var scene = new Scene(root);
            root.getChildren().add(view);
            stage.setScene(scene);

            updateBounds(stage, params.initialBounds());

            stage.setOnCloseRequest(event -> browser.close());
            browser.on(TitleChanged.class, event ->
                runLater(() -> stage.setTitle(event.title()))
            );
            browser.on(BrowserClosed.class, event ->
                runLater(stage::close)
            );
            browser.on(UpdateBoundsRequested.class, event ->
                runLater(() -> updateBounds(stage, event.bounds()))
            );
            stage.show();
        });
        return Response.proceed();
    }

    private static void updateBounds(Stage stage, Rect bounds) {
        if (bounds.size().isEmpty()) {
            stage.setWidth(DEFAULT_POPUP_WIDTH);
            stage.setHeight(DEFAULT_POPUP_HEIGHT);
        } else {
            stage.setX(bounds.origin().x());
            stage.setY(bounds.origin().y());
            stage.setWidth(bounds.size().width());
            stage.setHeight(bounds.size().height());
        }
    }
}

import com.teamdev.jxbrowser.browser.callback.OpenPopupCallback
import com.teamdev.jxbrowser.browser.event.BrowserClosed
import com.teamdev.jxbrowser.browser.event.TitleChanged
import com.teamdev.jxbrowser.browser.event.UpdateBoundsRequested
import com.teamdev.jxbrowser.dsl.subscribe
import com.teamdev.jxbrowser.ui.Rect
import com.teamdev.jxbrowser.view.javafx.BrowserView
import javafx.application.Platform.runLater
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.layout.StackPane
import javafx.stage.Stage

/**
 * JavaFX UI 工具包的默认 [OpenPopupCallback] 实现
 * 创建并显示带有嵌入式弹出 Browser 的新窗口。
 */
class DefaultOpenPopupCallbackJavaFx : OpenPopupCallback {

    override fun on(params: OpenPopupCallback.Params): OpenPopupCallback.Response {
        val browser = params.popupBrowser()
        runLater {
            val view = BrowserView.newInstance(browser)
            val stage = Stage()
            val root = StackPane()
            val scene = Scene(root)
            root.children.add(view)
            stage.scene = scene
            updateBounds(stage, params.initialBounds())
            stage.onCloseRequest = EventHandler { browser.close() }
            browser.subscribe<TitleChanged> { event ->
                runLater { stage.title = event.title() }
            }
            browser.subscribe<BrowserClosed> {
                runLater { stage.close() }
            }
            browser.subscribe<UpdateBoundsRequested> { event ->
                runLater { updateBounds(stage, event.bounds()) }
            }
            stage.show()
        }
        return OpenPopupCallback.Response.proceed()
    }

    companion object {
        private const val DEFAULT_POPUP_WIDTH = 800
        private const val DEFAULT_POPUP_HEIGHT = 600
        private fun updateBounds(stage: Stage, bounds: Rect) {
            if (bounds.size().isEmpty) {
                stage.width = DEFAULT_POPUP_WIDTH.toDouble()
                stage.height = DEFAULT_POPUP_HEIGHT.toDouble()
            } else {
                stage.x = bounds.origin().x().toDouble()
                stage.y = bounds.origin().y().toDouble()
                stage.width = bounds.size().width().toDouble()
                stage.height = bounds.size().height().toDouble()
            }
        }
    }
}

SWT

SWT BrowserView 的默认实现如下:

Java
Kotlin


import com.teamdev.jxbrowser.browser.callback.OpenPopupCallback;
import com.teamdev.jxbrowser.browser.event.BrowserClosed;
import com.teamdev.jxbrowser.browser.event.TitleChanged;
import com.teamdev.jxbrowser.browser.event.UpdateBoundsRequested;
import com.teamdev.jxbrowser.ui.Rect;
import com.teamdev.jxbrowser.view.swt.BrowserView;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;

/**
 * SWT UI 工具包的默认 {@link OpenPopupCallback} 实现
 * 创建并显示带有嵌入式弹出 Browser 的新窗口。
 */
public final class DefaultOpenPopupCallbackSwt implements OpenPopupCallback {

    private static final int DEFAULT_POPUP_WIDTH = 800;
    private static final int DEFAULT_POPUP_HEIGHT = 600;

    @Override
    public Response on(Params params) {
        var browser = params.popupBrowser();
        try {
            var display = Display.getDefault();
            display.asyncExec(() -> {
                var shell = new Shell(display);
                shell.setLayout(new FillLayout());
                var view = BrowserView.newInstance(shell, browser);
                updateBounds(shell, view, params.initialBounds());

                shell.addDisposeListener(event -> {
                    if (!browser.isClosed()) {
                        asyncExec(shell, browser::close);
                    }
                });

                browser.on(TitleChanged.class, event ->
                    asyncExec(shell, () -> shell.setText(event.title())));
                browser.on(BrowserClosed.class, event ->
                    asyncExec(shell, shell::dispose));
                browser.on(UpdateBoundsRequested.class, event ->
                    asyncExec(shell, () -> updateBounds(shell, view, event.bounds())));

                view.setVisible(true);
                shell.pack();
                shell.open();

                while (!shell.isDisposed()) {
                    if (!display.readAndDispatch()) {
                        display.sleep();
                    }
                }
            });
        } catch (SWTException ignore) {
            Response.proceed();
        }
        return Response.proceed();
    }

    private static void updateBounds(Shell shell, BrowserView view, Rect bounds) {
        shell.setLocation(bounds.x(), bounds.y());
        if (bounds.size().isEmpty()) {
            view.setSize(DEFAULT_POPUP_WIDTH, DEFAULT_POPUP_HEIGHT);
        } else {
            view.setSize(bounds.width(), bounds.height());
        }
    }

    private static void asyncExec(Widget widget, Runnable doRun) {
        widget.getDisplay().asyncExec(() -> {
            if (!widget.isDisposed()) {
                doRun.run();
            }
        });
    }
}

import com.teamdev.jxbrowser.browser.Browser
import com.teamdev.jxbrowser.browser.callback.OpenPopupCallback
import com.teamdev.jxbrowser.browser.event.BrowserClosed
import com.teamdev.jxbrowser.browser.event.TitleChanged
import com.teamdev.jxbrowser.browser.event.UpdateBoundsRequested
import com.teamdev.jxbrowser.dsl.subscribe
import com.teamdev.jxbrowser.ui.Rect
import com.teamdev.jxbrowser.view.swt.BrowserView
import org.eclipse.swt.SWTException
import org.eclipse.swt.layout.FillLayout
import org.eclipse.swt.widgets.Display
import org.eclipse.swt.widgets.Shell
import org.eclipse.swt.widgets.Widget

/**
 * SWT UI 工具包的默认 [OpenPopupCallback] 实现
 * 创建并显示带有嵌入式弹出 Browser 的新窗口。
 */
class DefaultOpenPopupCallbackSwt : OpenPopupCallback {
    override fun on(params: OpenPopupCallback.Params): OpenPopupCallback.Response {
        val browser: Browser = params.popupBrowser()
        try {
            val display: Display = Display.getDefault()
            display.asyncExec {
                val shell = Shell(display)
                shell.layout = FillLayout()
                val view = BrowserView.newInstance(shell, browser)
                updateBounds(shell, view, params.initialBounds())
                shell.addDisposeListener {
                    if (!browser.isClosed) {
                        asyncExec(shell) { browser.close() }
                    }
                }
                browser.subscribe<TitleChanged> { event ->
                    asyncExec(shell) { shell.text = event.title() }
                }
                browser.subscribe<BrowserClosed> {
                    asyncExec(shell, shell::dispose)
                }
                browser.subscribe<UpdateBoundsRequested> { event ->
                    asyncExec(shell) { updateBounds(shell, view, event.bounds()) }
                }
                view.isVisible = true
                shell.pack()
                shell.open()
                while (!shell.isDisposed) {
                    if (!display.readAndDispatch()) {
                        display.sleep()
                    }
                }
            }
        } catch (ignore: SWTException) {
            OpenPopupCallback.Response.proceed()
        }
        return OpenPopupCallback.Response.proceed()
    }

    companion object {
        private const val DEFAULT_POPUP_WIDTH = 800
        private const val DEFAULT_POPUP_HEIGHT = 600
        private fun updateBounds(shell: Shell, view: BrowserView, bounds: Rect) {
            shell.setLocation(bounds.x(), bounds.y())
            if (bounds.size().isEmpty) {
                view.setSize(DEFAULT_POPUP_WIDTH, DEFAULT_POPUP_HEIGHT)
            } else {
                view.setSize(bounds.width(), bounds.height())
            }
        }

        private fun asyncExec(widget: Widget, doRun: Runnable) {
            widget.display.asyncExec {
                if (!widget.isDisposed) {
                    doRun.run()
                }
            }
        }
    }
}

关闭弹出窗口

已打开的弹出窗口可以通过 Browser.close() 方法关闭,或者通过 JavaScript 中的 window.close() 关闭。在这两种情况下,都会触发 BrowserClosed 事件。

我们建议您始终注册 BrowserClosed 事件监听器,以便在弹出式窗口关闭时获得通知。这将允许您隐藏显示的弹出窗口。

Java
Kotlin

popup.on(BrowserClosed.class, event -> {
    // 隐藏弹出窗口。
});

popup.on(BrowserClosed::class.java) { event ->
    // 隐藏弹出窗口。
}