Upload Base
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
# eclipse
|
||||
bin
|
||||
*.launch
|
||||
.settings
|
||||
.metadata
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# idea
|
||||
out
|
||||
*.ipr
|
||||
*.iws
|
||||
*.iml
|
||||
.idea
|
||||
|
||||
# gradle
|
||||
build
|
||||
.gradle
|
||||
|
||||
# other
|
||||
eclipse
|
||||
run
|
||||
@@ -0,0 +1,32 @@
|
||||
This project contains modified and unmodified source files from the JCEF project https://code.google.com/p/javachromiumembedded/
|
||||
The following license applies to all file in the org.cef package, as well as all files in the native folder.
|
||||
|
||||
Copyright (c) 2008-2013 Marshall A. Greenblatt. Portions Copyright (c)
|
||||
2006-2009 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the name Chromium Embedded
|
||||
Framework nor the names of its contributors may be used to endorse
|
||||
or promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,36 @@
|
||||
# MCEF
|
||||
Minecraft Chromium Embedded Framework (MCEF) is an API to allow Minecraft Modders to add custom web browsers into Minecraft.
|
||||
The project was initialy made for WebDisplays (www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/1291044-web-displays-browse-on-the-internet-in-minecraft).
|
||||
It is based on JCEF (https://code.google.com/p/javachromiumembedded/), which is based on CEF (https://bitbucket.org/chromiumembedded/java-cef) which is based on chromium (http://www.chromium.org).
|
||||
|
||||
# Features
|
||||
- 2D & 3D web view rendering (not only in GUIs)
|
||||
- Java -> JavaScript (IBrowser.runJavaScript)
|
||||
- JavaScript -> Java (IJSQueryHandler)
|
||||
- Embedded files (mod://modname/file.html => /assets/modname/html/file.html)
|
||||
- HTML5, CSS3 are supported.
|
||||
|
||||
# What can I do with this?
|
||||
- If you're tired of Minecraft's GuiScreen, you can make a HTML/JS/CSS GUI.
|
||||
- Open a link to your wiki.
|
||||
- Whatever you want, but please not a WebDisplays clone.
|
||||
|
||||
# Currently supported platforms
|
||||
- Windows 10 x64
|
||||
- macOS (Intel-based Macs only) - needs work, frequent crashes
|
||||
- Linux x64 (tested on Fedora 34 and Ubuntu 20.04)
|
||||
|
||||
# For players
|
||||
This is the Github project of MCEF; here you can only read the source code of it.
|
||||
You can download the mod in its latest version from here: http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/2324969-minecraft-chromium-embedded-framework-mcef
|
||||
|
||||
# For modders
|
||||
**DONT** copy the net.montoyo.mcef.api package into your project. Instead, download the latest API release from https://github.com/montoyo/mcef/releases and put it in the libs folder. Users will have to download the mod from the MinecraftForum thread.
|
||||
To understand how it works, you may look at the net.montoyo.mcef.example package, which demos:
|
||||
* The IBrowser interface, how to draw it and control it
|
||||
* The IJSQueryHandler interface, how to handle JavaScript queries
|
||||
* The IDisplayHandler interface, how to handle browser URL changes
|
||||
* How to use the mod:// scheme
|
||||
|
||||
# For forkers
|
||||
Don't forget to add "-Dfml.coreMods.load=net.montoyo.mcef.coremod.ShutdownPatcher" to the VM options, otherwise the Java process will hang forever!
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://files.minecraftforge.net/maven' }
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:3.+'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'net.minecraftforge.gradle'
|
||||
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
version = '1.12.2-1.41'
|
||||
group = 'net.montoyo.mcef'
|
||||
archivesBaseName = 'mcef'
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
|
||||
minecraft {
|
||||
// The mappings can be changed at any time, and must be in the following format.
|
||||
// snapshot_YYYYMMDD Snapshot are built nightly.
|
||||
// stable_# Stables are built at the discretion of the MCP team.
|
||||
// Use non-default mappings at your own risk. they may not always work.
|
||||
// Simply re-run your setup task after changing the mappings to update your workspace.
|
||||
//mappings channel: 'snapshot', version: '20171003-1.12'
|
||||
mappings channel: 'snapshot', version: '20171003-1.12'
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
|
||||
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
|
||||
// Default run configurations.
|
||||
// These can be tweaked, removed, or duplicated as needed.
|
||||
runs {
|
||||
client {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
|
||||
|
||||
// Recommended logging level for the console
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
}
|
||||
|
||||
server {
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
|
||||
|
||||
// Recommended logging level for the console
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
|
||||
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
|
||||
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
|
||||
minecraft 'net.minecraftforge:forge:1.12.2-14.23.5.2855'
|
||||
|
||||
// You may put jars on which you depend on in ./libs or you may define them like so..
|
||||
// compile "some.group:artifact:version:classifier"
|
||||
// compile "some.group:artifact:version"
|
||||
|
||||
// Real examples
|
||||
// compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
|
||||
// compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
|
||||
|
||||
// The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
|
||||
// provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// These dependencies get remapped to your current MCP mappings
|
||||
// deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// For more info...
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
}
|
||||
|
||||
// Example for how to get properties into the manifest for reading by the runtime..
|
||||
jar {
|
||||
manifest {
|
||||
attributes([
|
||||
"Specification-Title" : "mcef",
|
||||
"Specification-Vendor" : "mcef",
|
||||
"Specification-Version" : "3", // We are version 3 of ourselves
|
||||
"Implementation-Title" : project.name,
|
||||
"Implementation-Version" : project.version,
|
||||
"Implementation-Vendor" : "mcef",
|
||||
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// Example configuration to allow publishing using the maven-publish task
|
||||
// This is the preferred method to reobfuscate your jar file
|
||||
jar.finalizedBy('reobfJar')
|
||||
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing
|
||||
//publish.dependsOn('reobfJar')
|
||||
|
||||
processResources {
|
||||
// this will ensure that this task is redone when the versions change.
|
||||
inputs.property "version", project.version
|
||||
inputs.property "mcversion", "${mc_version}"
|
||||
|
||||
// replace stuff in mcmod.info, nothing else
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include 'mcmod.info'
|
||||
|
||||
// replace version and mcversion
|
||||
expand 'version': project.version, 'mcversion': "${mc_version}"
|
||||
}
|
||||
|
||||
// copy everything else except the mcmod.info
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude 'mcmod.info'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
mc_version=1.12.2-14.23.5.2855
|
||||
Vendored
BIN
Binary file not shown.
+5
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,224 @@
|
||||
package net.montoyo.mcef;
|
||||
|
||||
import net.montoyo.mcef.api.*;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
public class BaseProxy implements API {
|
||||
|
||||
public void onPreInit() {
|
||||
}
|
||||
|
||||
public void onInit() {
|
||||
Log.info("MCEF is running on server. Nothing to do.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBrowser createBrowser(String url, boolean transparent) {
|
||||
Log.warning("A mod called API.createBrowser() from server! Returning null...");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBrowser createBrowser(String url) {
|
||||
return createBrowser(url, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDisplayHandler(IDisplayHandler idh) {
|
||||
Log.warning("A mod called API.registerDisplayHandler() from server!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openExampleBrowser(String url) {
|
||||
Log.warning("A mod called API.openExampleBrowser() from server! URL: %s", url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerJSQueryHandler(IJSQueryHandler iqh) {
|
||||
Log.warning("A mod called API.registerJSQueryHandler() from server!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mimeTypeFromExtension(String ext) {
|
||||
Log.warning("A mod called API.mimeTypeFromExtension() from server!");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerScheme(String name, Class<? extends IScheme> schemeClass, boolean std, boolean local, boolean displayIsolated, boolean secure, boolean corsEnabled, boolean cspBypassing, boolean fetchEnabled) {
|
||||
Log.warning("A mod called API.registerScheme() from server!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSchemeRegistered(String name) {
|
||||
Log.warning("A mod called API.isSchemeRegistered() from server!");
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onShutdown() {
|
||||
}
|
||||
|
||||
private static final int PUNYCODE_TMIN = 1;
|
||||
private static final int PUNYCODE_TMAX = 26;
|
||||
private static final int PUNYCODE_SKEW = 38;
|
||||
private static final int PUNYCODE_DAMP = 700;
|
||||
private static final int PUNYCODE_INITIAL_BIAS = 72;
|
||||
private static final int PUNYCODE_INITIAL_N = 128;
|
||||
|
||||
private static int punycodeBiasAdapt(int delta, int numPoints, boolean firstTime) {
|
||||
if(firstTime)
|
||||
delta /= PUNYCODE_DAMP;
|
||||
else
|
||||
delta /= 2;
|
||||
|
||||
int k = 0;
|
||||
delta = delta + delta / numPoints;
|
||||
|
||||
while(delta > ((36 - PUNYCODE_TMIN) * PUNYCODE_TMAX) / 2) {
|
||||
delta /= 36 - PUNYCODE_TMIN;
|
||||
k += 36;
|
||||
}
|
||||
|
||||
return k + ((36 - PUNYCODE_TMIN + 1) * delta) / (delta + PUNYCODE_SKEW);
|
||||
}
|
||||
|
||||
private static void punycodeEncodeNumber(StringBuilder dst, int q, int bias) {
|
||||
boolean keepGoing = true;
|
||||
|
||||
for(int k = 36; keepGoing; k += 36) {
|
||||
//Compute & clamp threshold
|
||||
int t = k - bias;
|
||||
if(t < PUNYCODE_TMIN)
|
||||
t = PUNYCODE_TMIN;
|
||||
else if(t > PUNYCODE_TMAX)
|
||||
t = PUNYCODE_TMAX;
|
||||
|
||||
//Compute digit
|
||||
int digit;
|
||||
if(q < t) {
|
||||
digit = q;
|
||||
keepGoing = false;
|
||||
} else {
|
||||
digit = t + (q - t) % (36 - t);
|
||||
q = (q - t) / (36 - t);
|
||||
}
|
||||
|
||||
//Encode digit
|
||||
if(digit < 26)
|
||||
dst.append((char) ('a' + digit));
|
||||
else
|
||||
dst.append((char) ('0' + digit - 26));
|
||||
}
|
||||
}
|
||||
|
||||
private static String punycodeEncodeString(int[] input) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
for(int i = 0; i < input.length; i++) {
|
||||
if(input[i] < 128)
|
||||
output.append((char) input[i]);
|
||||
}
|
||||
|
||||
int n = PUNYCODE_INITIAL_N;
|
||||
int delta = 0;
|
||||
int bias = PUNYCODE_INITIAL_BIAS;
|
||||
int h = output.length();
|
||||
int b = h;
|
||||
|
||||
if(b > 0)
|
||||
output.append('-');
|
||||
|
||||
while(h < input.length) {
|
||||
int m = Integer.MAX_VALUE;
|
||||
for(int i = 0; i < input.length; i++) {
|
||||
if(input[i] >= n && input[i] < m)
|
||||
m = input[i];
|
||||
}
|
||||
|
||||
delta = delta + (m - n) * (h + 1);
|
||||
n = m;
|
||||
|
||||
for(int i = 0; i < input.length; i++) {
|
||||
int c = input[i];
|
||||
|
||||
if(c < n)
|
||||
delta++;
|
||||
|
||||
if(c == n) {
|
||||
punycodeEncodeNumber(output, delta, bias);
|
||||
bias = punycodeBiasAdapt(delta, h + 1, h == b);
|
||||
delta = 0;
|
||||
h++;
|
||||
}
|
||||
}
|
||||
|
||||
delta++;
|
||||
n++;
|
||||
}
|
||||
|
||||
return "xn--" + output.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String punycode(String url) {
|
||||
int protoEnd = url.indexOf("://");
|
||||
|
||||
if(protoEnd < 0)
|
||||
protoEnd = 0;
|
||||
else
|
||||
protoEnd += 3;
|
||||
|
||||
int hostEnd = url.indexOf('/', protoEnd);
|
||||
if(hostEnd < 0)
|
||||
hostEnd = url.length();
|
||||
|
||||
String hostname = url.substring(protoEnd, hostEnd);
|
||||
boolean doTransform = false;
|
||||
|
||||
for(int i = 0; i < hostname.length(); i++) {
|
||||
if(hostname.charAt(i) >= 128) {
|
||||
doTransform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!doTransform)
|
||||
return url;
|
||||
|
||||
String[] parts = hostname.split("\\.");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean first = true;
|
||||
|
||||
sb.append(url, 0, protoEnd);
|
||||
|
||||
for(String p: parts) {
|
||||
doTransform = false;
|
||||
|
||||
for(int i = 0; i < p.length(); i++) {
|
||||
if(p.charAt(i) >= 128) {
|
||||
doTransform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
sb.append('.');
|
||||
|
||||
if(doTransform)
|
||||
sb.append(punycodeEncodeString(p.codePoints().toArray()));
|
||||
else
|
||||
sb.append(p);
|
||||
}
|
||||
|
||||
sb.append(url, hostEnd, url.length());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package net.montoyo.mcef;
|
||||
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LetsEncryptAdder {
|
||||
private static final String CERT_ALIAS = "lets-encrypt-x3-cross-signed";
|
||||
private static final String KEYSTORE_PASSWORD = "changeit";
|
||||
|
||||
public static void addLetsEncryptCertificate() throws Exception {
|
||||
try (InputStream certStream = LetsEncryptAdder.class
|
||||
.getResourceAsStream("/assets/letsencrypt/isrgrootx1.der")) {
|
||||
if (certStream == null) {
|
||||
throw new FileNotFoundException("Let's Encrypt root certificate file not found.");
|
||||
}
|
||||
|
||||
Path ksPath = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts");
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
|
||||
try (InputStream ksInputStream = Files.newInputStream(ksPath)) {
|
||||
keyStore.load(ksInputStream, KEYSTORE_PASSWORD.toCharArray());
|
||||
}
|
||||
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Certificate certificate;
|
||||
|
||||
try (BufferedInputStream caInput = new BufferedInputStream(certStream)) {
|
||||
certificate = cf.generateCertificate(caInput);
|
||||
}
|
||||
|
||||
keyStore.setCertificateEntry(CERT_ALIAS, certificate);
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(keyStore);
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, tmf.getTrustManagers(), null);
|
||||
SSLContext.setDefault(sslContext);
|
||||
}
|
||||
}
|
||||
|
||||
public static void doStuff() {
|
||||
String version = System.getProperty("java.version");
|
||||
Pattern versionPattern = Pattern.compile("^(\\d+)(?:\\.(\\d+))?.*?_?(\\d+)?");
|
||||
Matcher matcher = versionPattern.matcher(version);
|
||||
|
||||
int majorVersion = 7;
|
||||
int minorVersion = 110; // Default fallback
|
||||
|
||||
if (matcher.matches()) {
|
||||
majorVersion = Integer.parseInt(matcher.group(1));
|
||||
if (matcher.group(3) != null) {
|
||||
minorVersion = Integer.parseInt(matcher.group(3));
|
||||
}
|
||||
} else {
|
||||
Log.info("[Let'sEncrypt SSL] Failed to parse Java version. Applying fix anyway.");
|
||||
}
|
||||
|
||||
if ((majorVersion == 7 && minorVersion >= 111) || (majorVersion == 8 && minorVersion >= 101)) {
|
||||
Log.info("[Let'sEncrypt SSL] Not running as Java version is at least Java " + majorVersion + "u"
|
||||
+ minorVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Log.info("[Let'sEncrypt SSL] Adding Let's Encrypt certificate...");
|
||||
addLetsEncryptCertificate();
|
||||
Log.info("[Let'sEncrypt SSL] Done! Attempting to connect to https://helloworld.letsencrypt.org...");
|
||||
|
||||
URL url = new URL("https://helloworld.letsencrypt.org");
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.setConnectTimeout(5000);
|
||||
conn.setReadTimeout(5000);
|
||||
|
||||
String response;
|
||||
try (InputStream inputStream = conn.getInputStream()) {
|
||||
response = IOUtils.toString(inputStream);
|
||||
}
|
||||
|
||||
if (response.isEmpty()) {
|
||||
Log.error("[Let'sEncrypt SSL] Unknown error: Unable to access Let's Encrypt resources.");
|
||||
} else {
|
||||
Log.info("[Let'sEncrypt SSL] Success! You can now access Let's Encrypt-secured resources.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error("[Let'sEncrypt SSL] Failed to add the Let's Encrypt root certificate.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package net.montoyo.mcef;
|
||||
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.SidedProxy;
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import net.montoyo.mcef.LetsEncryptAdder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Mod(modid = "mcef", name = "MCEF", version = MCEF.VERSION)
|
||||
public class MCEF {
|
||||
|
||||
public static final String VERSION = "1.41";
|
||||
public static boolean ENABLE_EXAMPLE;
|
||||
public static boolean SKIP_UPDATES;
|
||||
public static boolean WARN_UPDATES;
|
||||
public static boolean USE_FORGE_SPLASH;
|
||||
public static String FORCE_MIRROR = null;
|
||||
public static String HOME_PAGE;
|
||||
private static String DEF_CEF_ARGS;
|
||||
public static String[] CEF_ARGS = new String[0];
|
||||
public static boolean CHECK_VRAM_LEAK;
|
||||
public static boolean SHUTDOWN_JCEF;
|
||||
public static boolean SECURE_MIRRORS_ONLY;
|
||||
|
||||
@Mod.Instance(owner = "mcef")
|
||||
public static MCEF INSTANCE;
|
||||
|
||||
@SidedProxy(serverSide = "net.montoyo.mcef.BaseProxy", clientSide = "net.montoyo.mcef.client.ClientProxy")
|
||||
public static BaseProxy PROXY;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void onPreInit(FMLPreInitializationEvent ev) {
|
||||
Log.info("PREINT, Loading MCEF config...");
|
||||
Configuration cfg = new Configuration(ev.getSuggestedConfigurationFile());
|
||||
|
||||
// Default CEF arguments
|
||||
DEF_CEF_ARGS = "--enable-widevine --disable-gpu --disable-gpu-compositing";
|
||||
|
||||
//Config: main
|
||||
SKIP_UPDATES = cfg.getBoolean("skipUpdates", "main", false, "Do not update binaries.");
|
||||
WARN_UPDATES = cfg.getBoolean("warnUpdates", "main", true, "Tells in the chat if a new version of MCEF is available.");
|
||||
USE_FORGE_SPLASH = cfg.getBoolean("useForgeSplash", "main", true, "Use Forge's splash screen to display resource download progress (may be unstable).");
|
||||
SHUTDOWN_JCEF = cfg.getBoolean("shutdownJcef", "main", false, "Set this to true if your Java process hangs after closing Minecraft. This is disabled by default because it makes the launcher think Minecraft crashed...");
|
||||
SECURE_MIRRORS_ONLY = cfg.getBoolean("secureMirrorsOnly", "main", true, "Only enable secure (HTTPS) mirror. This should be kept to true unless you know what you're doing.");
|
||||
|
||||
// Merge default and user CEF arguments:
|
||||
String[] defaultArgs = DEF_CEF_ARGS.split("\\s+");
|
||||
String[] userArgs = cfg.getString("cefArgs", "main", DEF_CEF_ARGS, "Command line arguments passed to CEF. For advanced users.").split("\\s+");
|
||||
|
||||
List<String> combinedArgs = new ArrayList<>(Arrays.asList(defaultArgs)); // Start with the default args
|
||||
combinedArgs.addAll(Arrays.asList(userArgs)); // Add the user-provided args
|
||||
|
||||
// Convert back to array
|
||||
CEF_ARGS = combinedArgs.toArray(new String[0]);
|
||||
|
||||
String mirror = cfg.getString("forcedMirror", "main", "", "A URL that contains every MCEF resources; for instance https://montoyo.net/jcef.").trim();
|
||||
if (mirror.length() > 0)
|
||||
FORCE_MIRROR = mirror;
|
||||
|
||||
//Config: exampleBrowser
|
||||
ENABLE_EXAMPLE = cfg.getBoolean("enable", "exampleBrowser", true, "Set this to false if you don't want to enable the F10 browser.");
|
||||
HOME_PAGE = cfg.getString("home", "exampleBrowser", "mod://mcef/home.html", "The home page of the F10 browser.");
|
||||
|
||||
//Config: debug
|
||||
CHECK_VRAM_LEAK = cfg.getBoolean("checkForVRAMLeak", "debug", false, "Track allocated OpenGL textures to make sure there's no leak");
|
||||
|
||||
// Save the configuration
|
||||
cfg.save();
|
||||
|
||||
// Add Let's Encrypt certificate
|
||||
try {
|
||||
LetsEncryptAdder.addLetsEncryptCertificate();
|
||||
} catch (Exception e) {
|
||||
Log.error("Failed to add Let's Encrypt certificate: " + e.getMessage());
|
||||
}
|
||||
|
||||
PROXY.onPreInit();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void onInit(FMLInitializationEvent ev) {
|
||||
Log.info("Now initializing MCEF v%s...", VERSION);
|
||||
PROXY.onInit();
|
||||
}
|
||||
|
||||
//Called by Minecraft.run() if the ShutdownPatcher succeeded
|
||||
public static void onMinecraftShutdown() {
|
||||
Log.info("Minecraft shutdown hook called!");
|
||||
PROXY.onShutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface API {
|
||||
|
||||
/**
|
||||
* Creates a web view and loads the specified URL.
|
||||
*
|
||||
* @param url The URL to start from.
|
||||
* @param transp True is the web view can be transparent
|
||||
* @return The created web view, or null if this is run on server.
|
||||
*/
|
||||
IBrowser createBrowser(String url, boolean transp);
|
||||
|
||||
/**
|
||||
* Same as {@link #createBrowser(String, boolean) createBrowser} but with transp set to false.
|
||||
*
|
||||
* @param url The URL to start from.
|
||||
* @return The created web view, or null if this is run on server.
|
||||
*/
|
||||
IBrowser createBrowser(String url);
|
||||
|
||||
/**
|
||||
* Registers a display handler.
|
||||
* @param idh The display handler to register.
|
||||
* @see IDisplayHandler
|
||||
*/
|
||||
void registerDisplayHandler(IDisplayHandler idh);
|
||||
|
||||
/**
|
||||
* Registers a JavaScript query handler.
|
||||
* @param iqh The JavaScript query handler to register.
|
||||
* @see IJSQueryHandler
|
||||
*/
|
||||
void registerJSQueryHandler(IJSQueryHandler iqh);
|
||||
|
||||
/**
|
||||
* Call this to know if MCEF is in virtual mode.
|
||||
* MCEF switches in virtual mode if something failed to load.
|
||||
* When in virtual mode, {@link #createBrowser(String, boolean) createBrowser} will generate fake browsers that does nothing.
|
||||
*
|
||||
* @return true if MCEF is in virtual mode.
|
||||
*/
|
||||
boolean isVirtual();
|
||||
|
||||
/**
|
||||
* Opens the example browser UI.
|
||||
* @param url The URL to load.
|
||||
* @see net.montoyo.mcef.example.ExampleMod
|
||||
*/
|
||||
void openExampleBrowser(String url);
|
||||
|
||||
/**
|
||||
* Returns a mime type from a file extension,
|
||||
* or null, if there is no mapping for this extension.
|
||||
*
|
||||
* @param ext File extension, without the '.'
|
||||
* @return A mime type corresponding to this extension, or null if none could be found.
|
||||
*/
|
||||
String mimeTypeFromExtension(String ext);
|
||||
|
||||
/**
|
||||
* Registers a scheme (custom URLs).
|
||||
* This has to be done in preInit, init happens too late.
|
||||
*
|
||||
* @param name The name of the scheme
|
||||
* @param schemeClass The class that will be instantiated for request
|
||||
* @param std Whether the scheme has standards URLs, like "protocol://host/path". Non standard is just "procotol:"
|
||||
* @param local If the scheme is local, some special security rules are applied, just like the "file:///" scheme.
|
||||
* @param displayIsolated iframes (and such things) from an external scheme cannot access pages from this scheme.
|
||||
* @param secure Whether this scheme is considered as secure (like https)
|
||||
* @param corsEnabled To allow this scheme to send CORS requests
|
||||
* @param cspBypassing true if this scheme can bypass Content-Security-Policy (CSP) checks
|
||||
* @param fetchEnabled true if this scheme can perform Fetch API requests
|
||||
*
|
||||
* @see org.cef.callback.CefSchemeRegistrar
|
||||
*/
|
||||
void registerScheme(String name, Class<? extends IScheme> schemeClass, boolean std, boolean local, boolean displayIsolated, boolean secure, boolean corsEnabled, boolean cspBypassing, boolean fetchEnabled);
|
||||
|
||||
/**
|
||||
* Checks whether the scheme with name 'name' is already registered.
|
||||
*
|
||||
* @param name The name of the scheme
|
||||
* @return true if it is registered, false otherwise.
|
||||
*/
|
||||
boolean isSchemeRegistered(String name);
|
||||
|
||||
/**
|
||||
* If the hostname of the passed URL contains some illegal characters, it converts the hostname to punnycode.
|
||||
* If it fails to parse the URL or if the specified URL contains a valid hostname, returns the input string.
|
||||
*
|
||||
* @param url The URL to transform
|
||||
* @return The transformed URL
|
||||
*/
|
||||
String punycode(String url);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface IBrowser {
|
||||
|
||||
/**
|
||||
* Destroys the web view.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Resizes the web view.
|
||||
*
|
||||
* @param width The new width in pixels.
|
||||
* @param height The new height in pixels.
|
||||
*/
|
||||
void resize(int width, int height);
|
||||
|
||||
/**
|
||||
* Renders the web view into Minecraft.
|
||||
*
|
||||
* @param x1 The first X coord of the rectangle to render the web view on (left).
|
||||
* @param y1 The first Y coord of the rectangle to render the web view on (top).
|
||||
* @param x2 The second X coord of the rectangle to render the web view on (right).
|
||||
* @param y2 The second Y coord of the rectangle to render the web view on (bottom).
|
||||
*/
|
||||
void draw(double x1, double y1, double x2, double y2);
|
||||
|
||||
/**
|
||||
* Gets the OpenGL texture ID of the web view.
|
||||
* @return the OpenGL texture ID of the web view.
|
||||
*/
|
||||
int getTextureID();
|
||||
|
||||
/**
|
||||
* Simulates a mouse move.
|
||||
*
|
||||
* @param x The X coord of the mouse.
|
||||
* @param y The Y coord of the mouse.
|
||||
* @param mods The key modifiers (shift, ctrl, alt)
|
||||
* @param left true if the mouse is out of (left) the web view.
|
||||
*/
|
||||
void injectMouseMove(int x, int y, int mods, boolean left);
|
||||
|
||||
/**
|
||||
* Simulates a mouse click.
|
||||
*
|
||||
* @param x The X coord of the mouse.
|
||||
* @param y The Y coord of the mouse.
|
||||
* @param mods The key modifiers (shift, ctrl, alt)
|
||||
* @param btn The mouse button to press. See {@link java.awt.event.MouseEvent}
|
||||
* @param pressed true if the button is pressed, false if it is released.
|
||||
* @param ccnt The click count. You probably want this to be 1.
|
||||
*/
|
||||
void injectMouseButton(int x, int y, int mods, int btn, boolean pressed, int ccnt);
|
||||
|
||||
/**
|
||||
* Simulates a keyboard type.
|
||||
*
|
||||
* @param c The typed character.
|
||||
* @param mods The key modifiers (shift, ctrl, alt)
|
||||
*/
|
||||
void injectKeyTyped(char c, int mods);
|
||||
|
||||
/**
|
||||
* Simulates a key press.
|
||||
*
|
||||
* @param keyCode LWJGL key code of the pressed key.
|
||||
* @param c The resulting character
|
||||
* @param mods The key modifiers (shift, ctrl, alt)
|
||||
*/
|
||||
void injectKeyPressedByKeyCode(int keyCode, char c, int mods);
|
||||
|
||||
/**
|
||||
* Simulates a key release.
|
||||
*
|
||||
* @param keyCode LWJGL key code of the pressed key.
|
||||
* @param c The resulting character
|
||||
* @param mods The key modifiers (shift, ctrl, alt)
|
||||
*/
|
||||
void injectKeyReleasedByKeyCode(int keyCode, char c, int mods);
|
||||
|
||||
/**
|
||||
* Simulates a mouse wheel.
|
||||
*
|
||||
* @param x The X coord of the mouse.
|
||||
* @param y The Y coord of the mouse.
|
||||
* @param mods The key modifiers (shift, ctrl, alt)
|
||||
* @param amount The amount to scroll.
|
||||
* @param rot The number of "clicks" by which the mouse wheel was rotated.
|
||||
*/
|
||||
void injectMouseWheel(int x, int y, int mods, int amount, int rot);
|
||||
|
||||
/**
|
||||
* Runs JavaScript code on the web view.
|
||||
*
|
||||
* @param script The script to run.
|
||||
* @param frame The URL of the frame to run the script on. Let this EMPTY (!= null) for the default frame.
|
||||
*/
|
||||
void runJS(String script, String frame);
|
||||
|
||||
/**
|
||||
* Changes the current page's address.
|
||||
* @param url The URL to load.
|
||||
*/
|
||||
void loadURL(String url);
|
||||
|
||||
/**
|
||||
* Returns to the previous address.
|
||||
*/
|
||||
void goBack();
|
||||
|
||||
/**
|
||||
* Undoes {@link #goBack()}
|
||||
*/
|
||||
void goForward();
|
||||
|
||||
/**
|
||||
* Retrieves the current browser's location.
|
||||
* @return The current browser's URL.
|
||||
*/
|
||||
String getURL();
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves the current page's source code.
|
||||
* FIXME: This is asynchronous; so you won't get your source right after calling this method!
|
||||
*
|
||||
* @param isv An object that handles strings.
|
||||
*/
|
||||
void visitSource(IStringVisitor isv);
|
||||
|
||||
/**
|
||||
* Checks if the page is currently being loaded.
|
||||
*
|
||||
* @return true if the page is still being loaded, false otherwise.
|
||||
*/
|
||||
boolean isPageLoading();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface IDisplayHandler {
|
||||
|
||||
/**
|
||||
* Handle address changes.
|
||||
* @param browser The browser generating the event.
|
||||
* @param url The new URL.
|
||||
*/
|
||||
void onAddressChange(IBrowser browser, String url);
|
||||
|
||||
/**
|
||||
* Handle title changes.
|
||||
* @param browser The browser generating the event.
|
||||
* @param title The new title.
|
||||
*/
|
||||
void onTitleChange(IBrowser browser, String title);
|
||||
|
||||
/**
|
||||
* Called when the browser is about to display a tooltip.
|
||||
*
|
||||
* @param browser The browser generating the event.
|
||||
* @param text Contains the text that will be displayed in the tooltip.
|
||||
*/
|
||||
void onTooltip(IBrowser browser, String text);
|
||||
|
||||
/**
|
||||
* Called when the browser receives a status message.
|
||||
*
|
||||
* @param browser The browser generating the event.
|
||||
* @param value Contains the text that will be displayed in the status message.
|
||||
*/
|
||||
void onStatusMessage(IBrowser browser, String value);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
/**
|
||||
* Use this interface to answer to JavaScript queries.
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public interface IJSQueryCallback {
|
||||
|
||||
/**
|
||||
* If the query succeeded, call this.
|
||||
* @param response Whatever you want.
|
||||
*/
|
||||
void success(String response);
|
||||
|
||||
/**
|
||||
* If the query failed, call this.
|
||||
*
|
||||
* @param errId Whatever you want.
|
||||
* @param errMsg Whatever you want.
|
||||
*/
|
||||
void failure(int errId, String errMsg);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
/**
|
||||
* Use this to handle asynchronous JavaScript queries.
|
||||
* Queries are sent to Java using var request_id = window.mcefQuery({ request: 'my_request', persistent: false, onSuccess: function(response) {}, onFailure: function(error_code, error_message) {} })
|
||||
* Queries may be cancelled using window.mcefCancel(request_id)
|
||||
*
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public interface IJSQueryHandler {
|
||||
|
||||
/**
|
||||
* Handles a JavaScript query. Queries are created using the following JavaScript code:
|
||||
* var request_id = window.mcefQuery({ request: 'my_request', persistent: false, onSuccess: function(response) {}, onFailure: function(error_code, error_message) {} })
|
||||
*
|
||||
* Be aware that this method is NOT called from the main Minecraft thread, and you have to handle synchronisation yourself.
|
||||
*
|
||||
* @param b The browser the query has been created from.
|
||||
* @param queryId The unique query identifier.
|
||||
* @param query The "request" field.
|
||||
* @param persistent If the query is persistent or not. If it is, then cb will remain valid when leaving this method.
|
||||
* @param cb Use this to answer the query; this means call the JS onSuccess and onFailure functions.
|
||||
* @return true if the query was handled.
|
||||
*/
|
||||
boolean handleQuery(IBrowser b, long queryId, String query, boolean persistent, IJSQueryCallback cb);
|
||||
|
||||
/**
|
||||
* Handles a JavaScript query cancellation. Queries are cancelled using the following JavaScript code:
|
||||
* window.mcefCancel(request_id)
|
||||
*
|
||||
* Be aware that this method is NOT called from the main Minecraft thread, and you have to handle synchronisation yourself.
|
||||
*
|
||||
* @param b The browser the query has been cancelled from.
|
||||
* @param queryId The unique query identifier given in {@link #handleQuery(IBrowser, long, String, boolean, IJSQueryCallback)}
|
||||
*/
|
||||
void cancelQuery(IBrowser b, long queryId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface IScheme {
|
||||
|
||||
SchemePreResponse processRequest(String url);
|
||||
void getResponseHeaders(ISchemeResponseHeaders resp);
|
||||
boolean readResponse(ISchemeResponseData data);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface ISchemeResponseData {
|
||||
|
||||
byte[] getDataArray();
|
||||
int getBytesToRead();
|
||||
void setAmountRead(int rd);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface ISchemeResponseHeaders {
|
||||
|
||||
void setMimeType(String mt);
|
||||
void setStatus(int status);
|
||||
void setStatusText(String st);
|
||||
void setResponseLength(int len);
|
||||
void setRedirectURL(String redirURL);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public interface IStringVisitor {
|
||||
|
||||
void visit(String str);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
|
||||
public class MCEFApi {
|
||||
|
||||
/**
|
||||
* Call this to get the API instance.
|
||||
* @return the MCEF API or null if something failed.
|
||||
*/
|
||||
public static API getAPI() {
|
||||
try {
|
||||
Class cls = Class.forName("net.montoyo.mcef.MCEF");
|
||||
return (API) cls.getField("PROXY").get(null);
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if MCEF was loaded by forge.
|
||||
* @return true if it is loaded. false otherwise.
|
||||
*/
|
||||
public static boolean isMCEFLoaded() {
|
||||
return Loader.isModLoaded("mcef");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.montoyo.mcef.api;
|
||||
|
||||
public enum SchemePreResponse {
|
||||
|
||||
NOT_HANDLED,
|
||||
HANDLED_CONTINUE,
|
||||
HANDLED_CANCEL
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.api.IScheme;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
import org.cef.CefApp;
|
||||
import org.cef.browser.CefBrowser;
|
||||
import org.cef.browser.CefFrame;
|
||||
import org.cef.callback.CefSchemeHandlerFactory;
|
||||
import org.cef.callback.CefSchemeRegistrar;
|
||||
import org.cef.handler.CefAppHandlerAdapter;
|
||||
import org.cef.handler.CefResourceHandler;
|
||||
import org.cef.network.CefRequest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AppHandler extends CefAppHandlerAdapter {
|
||||
|
||||
public AppHandler() {
|
||||
super(new String[] {});
|
||||
}
|
||||
|
||||
private static class SchemeData {
|
||||
|
||||
private Class<? extends IScheme> cls;
|
||||
private boolean std;
|
||||
private boolean local;
|
||||
private boolean dispIsolated;
|
||||
private boolean secure;
|
||||
private boolean corsEnabled;
|
||||
private boolean cspBypassing;
|
||||
private boolean fetchEnabled;
|
||||
|
||||
private SchemeData(Class<? extends IScheme> cls, boolean std, boolean local, boolean dispIsolated, boolean secure, boolean corsEnabled, boolean cspBypassing, boolean fetchEnabled) {
|
||||
this.cls = cls;
|
||||
this.std = std;
|
||||
this.local = local;
|
||||
this.dispIsolated = dispIsolated;
|
||||
this.secure = secure;
|
||||
this.corsEnabled = corsEnabled;
|
||||
this.cspBypassing = cspBypassing;
|
||||
this.fetchEnabled = fetchEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final HashMap<String, SchemeData> schemeMap = new HashMap<>();
|
||||
|
||||
public void registerScheme(String name, Class<? extends IScheme> cls, boolean std, boolean local, boolean dispIsolated, boolean secure, boolean corsEnabled, boolean cspBypassing, boolean fetchEnabled) {
|
||||
schemeMap.put(name, new SchemeData(cls, std, local, dispIsolated, secure, corsEnabled, cspBypassing, fetchEnabled));
|
||||
}
|
||||
|
||||
public boolean isSchemeRegistered(String name) {
|
||||
return schemeMap.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegisterCustomSchemes(CefSchemeRegistrar reg) {
|
||||
int cnt = 0;
|
||||
|
||||
for(Map.Entry<String, SchemeData> entry : schemeMap.entrySet()) {
|
||||
SchemeData v = entry.getValue();
|
||||
|
||||
if(reg.addCustomScheme(entry.getKey(), v.std, v.local, v.dispIsolated, v.secure, v.corsEnabled, v.cspBypassing, v.fetchEnabled))
|
||||
cnt++;
|
||||
else
|
||||
Log.error("Could not register scheme %s", entry.getKey());
|
||||
}
|
||||
|
||||
Log.info("%d schemes registered", cnt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContextInitialized() {
|
||||
CefApp app = ((ClientProxy) MCEF.PROXY).getCefApp();
|
||||
|
||||
for(Map.Entry<String, SchemeData> entry : schemeMap.entrySet())
|
||||
app.registerSchemeHandlerFactory(entry.getKey(), "", new SchemeHandlerFactory(entry.getValue().cls));
|
||||
}
|
||||
|
||||
private static class SchemeHandlerFactory implements CefSchemeHandlerFactory {
|
||||
|
||||
private Class<? extends IScheme> cls;
|
||||
|
||||
private SchemeHandlerFactory(Class<? extends IScheme> cls) {
|
||||
this.cls = cls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefResourceHandler create(CefBrowser browser, CefFrame frame, String schemeName, CefRequest request) {
|
||||
try {
|
||||
return new SchemeResourceHandler(cls.newInstance());
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.client.SplashProgress;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.montoyo.mcef.BaseProxy;
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.api.IBrowser;
|
||||
import net.montoyo.mcef.api.IDisplayHandler;
|
||||
import net.montoyo.mcef.api.IJSQueryHandler;
|
||||
import net.montoyo.mcef.api.IScheme;
|
||||
import net.montoyo.mcef.coremod.ShutdownPatcher;
|
||||
import net.montoyo.mcef.example.ExampleMod;
|
||||
import net.montoyo.mcef.remote.RemoteConfig;
|
||||
import net.montoyo.mcef.utilities.ForgeProgressListener;
|
||||
import net.montoyo.mcef.utilities.IProgressListener;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import net.montoyo.mcef.utilities.Util;
|
||||
import net.montoyo.mcef.virtual.VirtualBrowser;
|
||||
import org.cef.CefApp;
|
||||
import org.cef.CefClient;
|
||||
import org.cef.CefSettings;
|
||||
import org.cef.OS;
|
||||
import org.cef.browser.CefBrowser;
|
||||
import org.cef.browser.CefBrowserOsr;
|
||||
import org.cef.browser.CefMessageRouter;
|
||||
import org.cef.browser.CefMessageRouter.CefMessageRouterConfig;
|
||||
import org.cef.browser.CefRenderer;
|
||||
import org.cef.handler.CefLifeSpanHandlerAdapter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ClientProxy extends BaseProxy {
|
||||
|
||||
public static String ROOT = ".";
|
||||
public static String JCEF_ROOT = ".";
|
||||
public static boolean VIRTUAL = false;
|
||||
|
||||
private CefApp cefApp;
|
||||
private CefClient cefClient;
|
||||
private CefMessageRouter cefRouter;
|
||||
private final ArrayList<CefBrowserOsr> browsers = new ArrayList<>();
|
||||
private String updateStr;
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final DisplayHandler displayHandler = new DisplayHandler();
|
||||
private final HashMap<String, String> mimeTypeMap = new HashMap<>();
|
||||
private final AppHandler appHandler = new AppHandler();
|
||||
private ExampleMod exampleMod;
|
||||
|
||||
@Override
|
||||
public void onPreInit() {
|
||||
exampleMod = new ExampleMod();
|
||||
exampleMod.onPreInit(); //Do it even if example mod is disabled because it registers the "mod://" scheme
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit() {
|
||||
appHandler.setArgs(MCEF.CEF_ARGS);
|
||||
|
||||
boolean enableForgeSplash = false;
|
||||
try {
|
||||
Field f = SplashProgress.class.getDeclaredField("enabled");
|
||||
f.setAccessible(true);
|
||||
enableForgeSplash = f.getBoolean(null);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
ROOT = mc.mcDataDir.getAbsolutePath().replaceAll("\\\\", "/");
|
||||
if (ROOT.endsWith("."))
|
||||
ROOT = ROOT.substring(0, ROOT.length() - 1);
|
||||
|
||||
if (ROOT.endsWith("/"))
|
||||
ROOT = ROOT.substring(0, ROOT.length() - 1);
|
||||
|
||||
JCEF_ROOT = ROOT + "/jcef";
|
||||
|
||||
File fileListing = new File(new File(ROOT), "config");
|
||||
|
||||
IProgressListener ipl;
|
||||
RemoteConfig cfg = new RemoteConfig();
|
||||
if (MCEF.USE_FORGE_SPLASH && enableForgeSplash)
|
||||
ipl = new ForgeProgressListener();
|
||||
else
|
||||
ipl = new UpdateFrame();
|
||||
|
||||
cfg.load();
|
||||
|
||||
if (!cfg.downloadMCEF(ipl)) {
|
||||
Log.warning("Going in virtual mode; couldn't download resources.");
|
||||
VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
|
||||
updateStr = cfg.getUpdateString();
|
||||
ipl.onProgressEnd();
|
||||
|
||||
if (OS.isLinux()) {
|
||||
File subproc = new File(JCEF_ROOT, "jcef_helper");
|
||||
|
||||
// Attempt to make the CEF subprocess executable if not
|
||||
if (!subproc.canExecute()) {
|
||||
try {
|
||||
int retCode = Runtime.getRuntime().exec(new String[]{"/usr/bin/chmod", "+x", subproc.getAbsolutePath()}).waitFor();
|
||||
|
||||
if (retCode != 0)
|
||||
throw new RuntimeException("chmod exited with code " + retCode);
|
||||
} catch (Throwable t) {
|
||||
Log.errorEx("Error while giving execution rights to jcef_helper. MCEF will enter virtual mode. You can fix this by chmoding jcef_helper manually.", t);
|
||||
VIRTUAL = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (VIRTUAL)
|
||||
return;
|
||||
|
||||
CefSettings settings = new CefSettings();
|
||||
settings.windowless_rendering_enabled = true;
|
||||
settings.background_color = settings.new ColorType(0, 255, 255, 255);
|
||||
settings.cache_path = (new File(JCEF_ROOT, "cache")).getAbsolutePath();
|
||||
|
||||
CefApp.startup(MCEF.CEF_ARGS);
|
||||
cefApp = CefApp.getInstance(settings);
|
||||
|
||||
// Custom scheme broken on Linux, for now
|
||||
if (!OS.isLinux()) {
|
||||
CefApp.addAppHandler(appHandler);
|
||||
}
|
||||
|
||||
loadMimeTypeMapping();
|
||||
|
||||
cefClient = cefApp.createClient();
|
||||
|
||||
Log.info(cefApp.getVersion().toString());
|
||||
cefRouter = CefMessageRouter.create(new CefMessageRouterConfig("mcefQuery", "mcefCancel"));
|
||||
cefClient.addMessageRouter(cefRouter);
|
||||
cefClient.addDisplayHandler(displayHandler);
|
||||
cefClient.addLifeSpanHandler(new CefLifeSpanHandlerAdapter() {
|
||||
@Override
|
||||
public boolean doClose(CefBrowser browser) {
|
||||
browser.close(true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!ShutdownPatcher.didPatchSucceed()) {
|
||||
Log.warning("ShutdownPatcher failed to patch Minecraft.run() method; starting ShutdownThread...");
|
||||
(new ShutdownThread()).start();
|
||||
}
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
if (MCEF.ENABLE_EXAMPLE)
|
||||
exampleMod.onInit();
|
||||
|
||||
Log.info("MCEF loaded successfuly.");
|
||||
}
|
||||
|
||||
public CefApp getCefApp() {
|
||||
return cefApp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBrowser createBrowser(String url, boolean transp) {
|
||||
if (VIRTUAL)
|
||||
return new VirtualBrowser();
|
||||
|
||||
CefBrowserOsr ret = (CefBrowserOsr) cefClient.createBrowser(url, true, transp);
|
||||
ret.setCloseAllowed();
|
||||
ret.createImmediately();
|
||||
|
||||
browsers.add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDisplayHandler(IDisplayHandler idh) {
|
||||
displayHandler.addHandler(idh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return VIRTUAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openExampleBrowser(String url) {
|
||||
if (MCEF.ENABLE_EXAMPLE)
|
||||
exampleMod.showScreen(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerJSQueryHandler(IJSQueryHandler iqh) {
|
||||
if (!VIRTUAL)
|
||||
cefRouter.addHandler(new MessageRouter(iqh), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerScheme(String name, Class<? extends IScheme> schemeClass, boolean std, boolean local, boolean displayIsolated, boolean secure, boolean corsEnabled, boolean cspBypassing, boolean fetchEnabled) {
|
||||
appHandler.registerScheme(name, schemeClass, std, local, displayIsolated, secure, corsEnabled, cspBypassing, fetchEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSchemeRegistered(String name) {
|
||||
return appHandler.isSchemeRegistered(name);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTick(TickEvent.RenderTickEvent ev) {
|
||||
if (ev.phase == TickEvent.Phase.START) {
|
||||
mc.mcProfiler.startSection("MCEF");
|
||||
|
||||
if (cefApp != null)
|
||||
cefApp.N_DoMessageLoopWork();
|
||||
|
||||
for (CefBrowserOsr b : browsers)
|
||||
b.mcefUpdate();
|
||||
|
||||
displayHandler.update();
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onLogin(PlayerEvent.PlayerLoggedInEvent ev) {
|
||||
if (updateStr == null || !MCEF.WARN_UPDATES)
|
||||
return;
|
||||
|
||||
Style cs = new Style();
|
||||
cs.setColor(TextFormatting.LIGHT_PURPLE);
|
||||
|
||||
TextComponentString cct = new TextComponentString(updateStr);
|
||||
cct.setStyle(cs);
|
||||
|
||||
ev.player.sendMessage(cct);
|
||||
}
|
||||
|
||||
public void removeBrowser(CefBrowserOsr b) {
|
||||
browsers.remove(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBrowser createBrowser(String url) {
|
||||
return createBrowser(url, false);
|
||||
}
|
||||
|
||||
private void runMessageLoopFor(long ms) {
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
do {
|
||||
cefApp.N_DoMessageLoopWork();
|
||||
} while (System.currentTimeMillis() - start < ms);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown() {
|
||||
if (VIRTUAL)
|
||||
return;
|
||||
|
||||
Log.info("Shutting down JCEF...");
|
||||
CefBrowserOsr.CLEANUP = false; //Workaround
|
||||
|
||||
for (CefBrowserOsr b : browsers)
|
||||
b.close();
|
||||
|
||||
browsers.clear();
|
||||
|
||||
if (MCEF.CHECK_VRAM_LEAK)
|
||||
CefRenderer.dumpVRAMLeak();
|
||||
|
||||
runMessageLoopFor(100);
|
||||
CefApp.forceShutdownState();
|
||||
cefClient.dispose();
|
||||
|
||||
if (MCEF.SHUTDOWN_JCEF)
|
||||
cefApp.N_Shutdown();
|
||||
}
|
||||
|
||||
public void loadMimeTypeMapping() {
|
||||
Pattern p = Pattern.compile("^(\\S+)\\s+(\\S+)\\s*(\\S*)\\s*(\\S*)$");
|
||||
String line = "";
|
||||
int cLine = 0;
|
||||
mimeTypeMap.clear();
|
||||
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(ClientProxy.class.getResourceAsStream("/assets/mcef/mime.types")));
|
||||
|
||||
while (true) {
|
||||
cLine++;
|
||||
line = br.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
line = line.trim();
|
||||
if (!line.startsWith("#")) {
|
||||
Matcher m = p.matcher(line);
|
||||
if (!m.matches())
|
||||
continue;
|
||||
|
||||
mimeTypeMap.put(m.group(2), m.group(1));
|
||||
if (m.groupCount() >= 4 && !m.group(3).isEmpty()) {
|
||||
mimeTypeMap.put(m.group(3), m.group(1));
|
||||
|
||||
if (m.groupCount() >= 5 && !m.group(4).isEmpty())
|
||||
mimeTypeMap.put(m.group(4), m.group(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Util.close(br);
|
||||
} catch (Throwable e) {
|
||||
Log.error("[Mime Types] Error while parsing \"%s\" at line %d:", line, cLine);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.info("Loaded %d mime types", mimeTypeMap.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mimeTypeFromExtension(String ext) {
|
||||
ext = ext.toLowerCase();
|
||||
String ret = mimeTypeMap.get(ext);
|
||||
if (ret != null)
|
||||
return ret;
|
||||
|
||||
//If the mimeTypeMap couldn't be loaded, fall back to common things
|
||||
switch (ext) {
|
||||
case "htm":
|
||||
case "html":
|
||||
return "text/html";
|
||||
|
||||
case "css":
|
||||
return "text/css";
|
||||
|
||||
case "js":
|
||||
return "text/javascript";
|
||||
|
||||
case "png":
|
||||
return "image/png";
|
||||
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
return "image/jpeg";
|
||||
|
||||
case "gif":
|
||||
return "image/gif";
|
||||
|
||||
case "svg":
|
||||
return "image/svg+xml";
|
||||
|
||||
case "xml":
|
||||
return "text/xml";
|
||||
|
||||
case "txt":
|
||||
return "text/plain";
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.api.IDisplayHandler;
|
||||
|
||||
import org.cef.CefSettings;
|
||||
import org.cef.browser.CefBrowser;
|
||||
import org.cef.browser.CefBrowserOsr;
|
||||
import org.cef.browser.CefFrame;
|
||||
import org.cef.handler.CefDisplayHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DisplayHandler implements CefDisplayHandler {
|
||||
|
||||
private final ArrayList<IDisplayHandler> list = new ArrayList<>();
|
||||
private final ArrayList<EventData> queue = new ArrayList<>();
|
||||
|
||||
private enum EventType {
|
||||
|
||||
ADDRESS_CHANGE,
|
||||
TITLE_CHANGE,
|
||||
TOOLTIP,
|
||||
STATUS_MESSAGE;
|
||||
|
||||
}
|
||||
|
||||
private static final class EventData {
|
||||
|
||||
private final CefBrowser browser;
|
||||
private final String data;
|
||||
private final EventType type;
|
||||
|
||||
private EventData(CefBrowser b, String d, EventType t) {
|
||||
browser = b;
|
||||
data = d;
|
||||
type = t;
|
||||
}
|
||||
|
||||
private void execute(IDisplayHandler idh) {
|
||||
switch(type) {
|
||||
case ADDRESS_CHANGE:
|
||||
idh.onAddressChange((CefBrowserOsr) browser, data);
|
||||
break;
|
||||
|
||||
case TITLE_CHANGE:
|
||||
idh.onTitleChange((CefBrowserOsr) browser, data);
|
||||
break;
|
||||
|
||||
case TOOLTIP:
|
||||
idh.onTooltip((CefBrowserOsr) browser, data);
|
||||
break;
|
||||
|
||||
case STATUS_MESSAGE:
|
||||
idh.onStatusMessage((CefBrowserOsr) browser, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddressChange(CefBrowser browser, CefFrame frame, String url) {
|
||||
synchronized(queue) {
|
||||
queue.add(new EventData(browser, url, EventType.ADDRESS_CHANGE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleChange(CefBrowser browser, String title) {
|
||||
synchronized(queue) {
|
||||
queue.add(new EventData(browser, title, EventType.TITLE_CHANGE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTooltip(CefBrowser browser, String text) {
|
||||
synchronized(queue) {
|
||||
queue.add(new EventData(browser, text, EventType.TOOLTIP));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusMessage(CefBrowser browser, String value) {
|
||||
synchronized(queue) {
|
||||
queue.add(new EventData(browser, value, EventType.STATUS_MESSAGE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(CefBrowser browser, CefSettings.LogSeverity level, String message, String source, int line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCursorChange(CefBrowser browser, int cursorType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addHandler(IDisplayHandler h) {
|
||||
list.add(h);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
synchronized(queue) {
|
||||
while(!queue.isEmpty()) {
|
||||
EventData ed = queue.remove(0);
|
||||
|
||||
for(IDisplayHandler idh : list)
|
||||
ed.execute(idh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.api.IJSQueryHandler;
|
||||
|
||||
import org.cef.browser.CefBrowser;
|
||||
import org.cef.browser.CefBrowserOsr;
|
||||
import org.cef.browser.CefFrame;
|
||||
import org.cef.callback.CefQueryCallback;
|
||||
import org.cef.handler.CefMessageRouterHandlerAdapter;
|
||||
|
||||
public class MessageRouter extends CefMessageRouterHandlerAdapter {
|
||||
|
||||
private IJSQueryHandler handler;
|
||||
|
||||
public MessageRouter(IJSQueryHandler h) {
|
||||
handler = h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQuery(CefBrowser browser, CefFrame frame, long query_id, String request, boolean persistent, CefQueryCallback callback) {
|
||||
return handler.handleQuery((CefBrowserOsr) browser, query_id, request, persistent, new QueryCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueryCanceled(CefBrowser browser, CefFrame frame, long query_id) {
|
||||
handler.cancelQuery((CefBrowserOsr) browser, query_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import org.cef.callback.CefQueryCallback;
|
||||
import net.montoyo.mcef.api.IJSQueryCallback;
|
||||
|
||||
public class QueryCallback implements IJSQueryCallback {
|
||||
|
||||
private CefQueryCallback cb;
|
||||
|
||||
public QueryCallback(CefQueryCallback cb) {
|
||||
this.cb = cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success(String response) {
|
||||
cb.success(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(int errId, String errMsg) {
|
||||
cb.failure(errId, errMsg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.api.IScheme;
|
||||
import net.montoyo.mcef.api.SchemePreResponse;
|
||||
import org.cef.callback.CefCallback;
|
||||
import org.cef.handler.CefResourceHandlerAdapter;
|
||||
import org.cef.misc.IntRef;
|
||||
import org.cef.misc.StringRef;
|
||||
import org.cef.network.CefRequest;
|
||||
import org.cef.network.CefResponse;
|
||||
|
||||
public class SchemeResourceHandler extends CefResourceHandlerAdapter {
|
||||
|
||||
private final IScheme scheme;
|
||||
|
||||
public SchemeResourceHandler(IScheme scm) {
|
||||
scheme = scm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processRequest(CefRequest request, CefCallback callback) {
|
||||
SchemePreResponse resp = scheme.processRequest(request.getURL());
|
||||
|
||||
switch(resp) {
|
||||
case HANDLED_CONTINUE:
|
||||
callback.Continue();
|
||||
return true;
|
||||
|
||||
case HANDLED_CANCEL:
|
||||
callback.cancel();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getResponseHeaders(CefResponse response, IntRef response_length, StringRef redirectUrl) {
|
||||
scheme.getResponseHeaders(new SchemeResponseHeaders(response, response_length, redirectUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readResponse(byte[] data_out, int bytes_to_read, IntRef bytes_read, CefCallback callback) {
|
||||
return scheme.readResponse(new SchemeResponseData(data_out, bytes_to_read, bytes_read));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.api.ISchemeResponseData;
|
||||
import org.cef.misc.IntRef;
|
||||
|
||||
public class SchemeResponseData implements ISchemeResponseData {
|
||||
|
||||
private final byte[] data;
|
||||
private final int toRead;
|
||||
private final IntRef read;
|
||||
|
||||
public SchemeResponseData(byte[] data, int toRead, IntRef read) {
|
||||
this.data = data;
|
||||
this.toRead = toRead;
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getDataArray() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytesToRead() {
|
||||
return toRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAmountRead(int rd) {
|
||||
read.set(rd);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.api.ISchemeResponseHeaders;
|
||||
import org.cef.misc.IntRef;
|
||||
import org.cef.misc.StringRef;
|
||||
import org.cef.network.CefResponse;
|
||||
|
||||
public class SchemeResponseHeaders implements ISchemeResponseHeaders {
|
||||
|
||||
private final CefResponse response;
|
||||
private final IntRef length;
|
||||
private final StringRef redirURL;
|
||||
|
||||
public SchemeResponseHeaders(CefResponse r, IntRef l, StringRef url) {
|
||||
response = r;
|
||||
length = l;
|
||||
redirURL = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMimeType(String mt) {
|
||||
response.setMimeType(mt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
response.setStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusText(String st) {
|
||||
response.setStatusText(st);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResponseLength(int len) {
|
||||
length.set(len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectURL(String r) {
|
||||
redirURL.set(r);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import org.cef.CefApp;
|
||||
import org.cef.browser.CefBrowserOsr;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
public class ShutdownThread extends Thread {
|
||||
|
||||
private Field running = null;
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
public ShutdownThread() {
|
||||
super("MCEF-Shutdown");
|
||||
setDaemon(false);
|
||||
|
||||
try {
|
||||
Field[] fields = Minecraft.class.getDeclaredFields();
|
||||
|
||||
for(Field f: fields) {
|
||||
if(f.getType().equals(Boolean.TYPE)) {
|
||||
//Log.info("Minecraft.%s: %s", f.getName(), Modifier.toString(f.getModifiers()));
|
||||
|
||||
if(f.getModifiers() == Modifier.VOLATILE) {
|
||||
f.setAccessible(true);
|
||||
running = f;
|
||||
Log.info("volatile boolean Minecraft.running => %s", f.getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Throwable t) {
|
||||
Log.warning("Can't detect Minecraft shutdown:");
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if(running == null)
|
||||
return;
|
||||
|
||||
Log.info("Minecraft shutdown detection thread started.");
|
||||
|
||||
while(true) {
|
||||
try {
|
||||
if(!running.getBoolean(mc))
|
||||
break;
|
||||
} catch(Throwable t) {
|
||||
Log.warning("Can't detect Minecraft shutdown:");
|
||||
t.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sleep(100);
|
||||
} catch(Throwable t) {}
|
||||
}
|
||||
|
||||
MCEF.PROXY.onShutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import net.montoyo.mcef.api.IStringVisitor;
|
||||
|
||||
import org.cef.callback.CefStringVisitor;
|
||||
|
||||
public class StringVisitor implements CefStringVisitor {
|
||||
|
||||
IStringVisitor isv;
|
||||
|
||||
public StringVisitor(IStringVisitor isv) {
|
||||
this.isv = isv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String string) {
|
||||
isv.visit(string);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package net.montoyo.mcef.client;
|
||||
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import net.montoyo.mcef.utilities.IProgressListener;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import net.montoyo.mcef.utilities.Util;
|
||||
|
||||
public class UpdateFrame extends JFrame implements IProgressListener {
|
||||
|
||||
private JLabel label = new JLabel("Preparing...");
|
||||
private JProgressBar pbar = new JProgressBar();
|
||||
|
||||
public UpdateFrame() {
|
||||
setTitle("Minecraft ChromiumEF");
|
||||
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
JPanel lpane = new JPanel();
|
||||
lpane.setLayout(new BoxLayout(lpane, BoxLayout.LINE_AXIS));
|
||||
lpane.add(label);
|
||||
lpane.add(Box.createHorizontalGlue());
|
||||
|
||||
Dimension dim = new Dimension(5, 5);
|
||||
JPanel pane = new JPanel();
|
||||
|
||||
pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
|
||||
pane.add(lpane);
|
||||
pane.add(new Box.Filler(dim, dim, dim));
|
||||
pane.add(pbar);
|
||||
|
||||
setContentPane(pane);
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
SwingUtilities.updateComponentTreeUI(this);
|
||||
} catch(Throwable t) {
|
||||
Log.info("Note: couldn't set system look & feel.");
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
|
||||
dim = new Dimension(50, 26);
|
||||
pbar.setMinimumSize(dim);
|
||||
pbar.setPreferredSize(dim);
|
||||
|
||||
setMinimumSize(new Dimension(540, 90));
|
||||
pack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressed(double d) {
|
||||
int val = (int) Util.clamp(d, 0.d, 100.d);
|
||||
pbar.setValue(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskChanged(String name) {
|
||||
Log.info("Task changed to \"%s\"", name);
|
||||
label.setText(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressEnd() {
|
||||
dispose();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package net.montoyo.mcef.coremod;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@IFMLLoadingPlugin.Name(value = "ShutdownPatcher")
|
||||
@IFMLLoadingPlugin.TransformerExclusions(value = "net.montoyo.mcef.")
|
||||
@IFMLLoadingPlugin.SortingIndex(value = 90007531) //It has to run after the searge-name transformation
|
||||
@IFMLLoadingPlugin.MCVersion("1.12.2")
|
||||
public class ShutdownPatcher implements IFMLLoadingPlugin, IClassTransformer {
|
||||
|
||||
private static boolean PATCH_OK = false;
|
||||
private static final String OBF_SHUTDOWN_METHOD = "func_71405_e"; //The "searge-obfuscated" name of the Minecraft.shutdownMinecraftApplet() method
|
||||
|
||||
public static boolean didPatchSucceed() {
|
||||
return PATCH_OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
return new String[] { "net.montoyo.mcef.coremod.ShutdownPatcher" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String deobfName, byte[] cls) {
|
||||
if(!deobfName.equals("net.minecraft.client.Minecraft"))
|
||||
return cls;
|
||||
|
||||
boolean envObf = !name.equals(deobfName); //If the current environment is obfuscated
|
||||
log("Now transforming %s, aka %s (obfuscated: %s)", name, deobfName, envObf ? "yes" : "no");
|
||||
|
||||
try {
|
||||
ClassReader cr = new ClassReader(cls);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
McVisitor cv = new McVisitor(cw, envObf);
|
||||
cr.accept(cv, 0);
|
||||
|
||||
return cw.toByteArray();
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
log("Failed to setup Minecraft shutdown detector.");
|
||||
}
|
||||
|
||||
return cls; //Abort class transforming
|
||||
}
|
||||
|
||||
private static class ShutdownMCAppletVisitor extends MethodVisitor {
|
||||
|
||||
public ShutdownMCAppletVisitor(MethodVisitor mv) {
|
||||
super(Opcodes.ASM5, mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCode() {
|
||||
mv.visitCode();
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "net/montoyo/mcef/MCEF", "onMinecraftShutdown", "()V", false);
|
||||
PATCH_OK = true;
|
||||
log("Target section has been patched.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class McVisitor extends ClassVisitor {
|
||||
|
||||
private final boolean envObf;
|
||||
|
||||
public McVisitor(ClassVisitor cv, boolean obf) {
|
||||
super(Opcodes.ASM5, cv);
|
||||
envObf = obf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
|
||||
if(access == Opcodes.ACC_PUBLIC && desc.equals("()V")) { //void shutdownMinecraftApplet()
|
||||
if((envObf && name.equals(OBF_SHUTDOWN_METHOD)) || name.equals("shutdownMinecraftApplet")) {
|
||||
log("shutdownMinecraftApplet() method found; transforming...");
|
||||
return new ShutdownMCAppletVisitor(mv);
|
||||
}
|
||||
}
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void log(String str, Object ... args) {
|
||||
LogManager.getLogger("MCEF").log(Level.INFO, String.format(str, args));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package net.montoyo.mcef.example;
|
||||
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.montoyo.mcef.api.API;
|
||||
import net.montoyo.mcef.api.IBrowser;
|
||||
import net.montoyo.mcef.api.MCEFApi;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class BrowserScreen extends GuiScreen {
|
||||
|
||||
IBrowser browser = null;
|
||||
private GuiButton back = null;
|
||||
private GuiButton fwd = null;
|
||||
private GuiButton go = null;
|
||||
private GuiButton min = null;
|
||||
private GuiButton vidMode = null;
|
||||
private GuiTextField url = null;
|
||||
private String urlToLoad = null;
|
||||
|
||||
private static final String YT_REGEX1 = "^https?://(?:www\\.)?youtube\\.com/watch\\?v=([a-zA-Z0-9_\\-]+)$";
|
||||
private static final String YT_REGEX2 = "^https?://(?:www\\.)?youtu\\.be/([a-zA-Z0-9_\\-]+)$";
|
||||
private static final String YT_REGEX3 = "^https?://(?:www\\.)?youtube\\.com/embed/([a-zA-Z0-9_\\-]+)(\\?.+)?$";
|
||||
|
||||
public BrowserScreen() {
|
||||
urlToLoad = MCEF.HOME_PAGE;
|
||||
}
|
||||
|
||||
public BrowserScreen(String url) {
|
||||
urlToLoad = (url == null) ? MCEF.HOME_PAGE : url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
ExampleMod.INSTANCE.hudBrowser = null;
|
||||
|
||||
if(browser == null) {
|
||||
//Grab the API and make sure it isn't null.
|
||||
API api = MCEFApi.getAPI();
|
||||
if(api == null)
|
||||
return;
|
||||
|
||||
//Create a browser and resize it to fit the screen
|
||||
browser = api.createBrowser((urlToLoad == null) ? MCEF.HOME_PAGE : urlToLoad, false);
|
||||
urlToLoad = null;
|
||||
}
|
||||
|
||||
//Resize the browser if window size changed
|
||||
if(browser != null)
|
||||
browser.resize(mc.displayWidth, mc.displayHeight - scaleY(20));
|
||||
|
||||
//Create GUI
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
buttonList.clear();
|
||||
|
||||
if(url == null) {
|
||||
buttonList.add(back = (new GuiButton(0, 0, 0, 20, 20, "<")));
|
||||
buttonList.add(fwd = (new GuiButton(1, 20, 0, 20, 20, ">")));
|
||||
buttonList.add(go = (new GuiButton(2, width - 60, 0, 20, 20, "Go")));
|
||||
buttonList.add(min = (new GuiButton(3, width - 20, 0, 20, 20, "_")));
|
||||
buttonList.add(vidMode = (new GuiButton(4, width - 40, 0, 20, 20, "YT")));
|
||||
vidMode.enabled = false;
|
||||
|
||||
url = new GuiTextField(5, fontRenderer, 40, 0, width - 100, 20);
|
||||
url.setMaxStringLength(65535);
|
||||
//url.setText("mod://mcef/home.html");
|
||||
} else {
|
||||
buttonList.add(back);
|
||||
buttonList.add(fwd);
|
||||
buttonList.add(go);
|
||||
buttonList.add(min);
|
||||
buttonList.add(vidMode);
|
||||
|
||||
//Handle resizing
|
||||
vidMode.x = width - 40;
|
||||
go.x = width - 60;
|
||||
min.x = width - 20;
|
||||
|
||||
String old = url.getText();
|
||||
url = new GuiTextField(5, fontRenderer, 40, 0, width - 100, 20);
|
||||
url.setMaxStringLength(65535);
|
||||
url.setText(old);
|
||||
}
|
||||
}
|
||||
|
||||
public int scaleY(int y) {
|
||||
double sy = ((double) y) / ((double) height) * ((double) mc.displayHeight);
|
||||
return (int) sy;
|
||||
}
|
||||
|
||||
public void loadURL(String url) {
|
||||
if(browser == null)
|
||||
urlToLoad = url;
|
||||
else
|
||||
browser.loadURL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScreen() {
|
||||
if(urlToLoad != null && browser != null) {
|
||||
browser.loadURL(urlToLoad);
|
||||
urlToLoad = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int i1, int i2, float f) {
|
||||
//Render the URL box first because it overflows a bit
|
||||
url.drawTextBox();
|
||||
|
||||
//Render buttons
|
||||
super.drawScreen(i1, i2, f);
|
||||
|
||||
//Renders the browser if itsn't null
|
||||
if(browser != null) {
|
||||
GlStateManager.disableDepth();
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
browser.draw(.0d, height, width, 20.d); //Don't forget to flip Y axis.
|
||||
GlStateManager.enableDepth();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
//Make sure to close the browser when you don't need it anymore.
|
||||
if(!ExampleMod.INSTANCE.hasBackup() && browser != null)
|
||||
browser.close();
|
||||
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInput() {
|
||||
while(Keyboard.next()) {
|
||||
if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
|
||||
mc.displayGuiScreen(null);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean pressed = Keyboard.getEventKeyState();
|
||||
boolean focused = url.isFocused();
|
||||
char key = Keyboard.getEventCharacter();
|
||||
int num = Keyboard.getEventKey();
|
||||
|
||||
if(browser != null && !focused) { //Inject events into browser
|
||||
if(pressed)
|
||||
browser.injectKeyPressedByKeyCode(num, key, 0);
|
||||
else
|
||||
browser.injectKeyReleasedByKeyCode(num, key, 0);
|
||||
|
||||
if(key != 0)
|
||||
browser.injectKeyTyped(key, 0);
|
||||
}
|
||||
|
||||
//Forward event to text box.
|
||||
if(!pressed && focused && num == Keyboard.KEY_RETURN)
|
||||
actionPerformed(go);
|
||||
else if(pressed)
|
||||
url.textboxKeyTyped(key, num);
|
||||
}
|
||||
|
||||
while(Mouse.next()) {
|
||||
int btn = Mouse.getEventButton();
|
||||
boolean pressed = Mouse.getEventButtonState();
|
||||
int sx = Mouse.getEventX();
|
||||
int sy = Mouse.getEventY();
|
||||
int wheel = Mouse.getEventDWheel();
|
||||
|
||||
if(browser != null) { //Inject events into browser. TODO: Handle mods & leaving.
|
||||
int y = mc.displayHeight - sy - scaleY(20); //Don't forget to flip Y axis.
|
||||
|
||||
if(wheel != 0)
|
||||
browser.injectMouseWheel(sx, y, 0, 1, wheel);
|
||||
else if(btn == -1)
|
||||
browser.injectMouseMove(sx, y, 0, y < 0);
|
||||
else
|
||||
browser.injectMouseButton(sx, y, 0, btn + 1, pressed, 1);
|
||||
}
|
||||
|
||||
if(pressed) { //Forward events to GUI.
|
||||
int x = sx * width / mc.displayWidth;
|
||||
int y = height - (sy * height / mc.displayHeight) - 1;
|
||||
|
||||
try {
|
||||
mouseClicked(x, y, btn);
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
url.mouseClicked(x, y, btn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Called by ExampleMod when the current browser's URL changes.
|
||||
public void onUrlChanged(IBrowser b, String nurl) {
|
||||
if(b == browser && url != null) {
|
||||
url.setText(nurl);
|
||||
vidMode.enabled = nurl.matches(YT_REGEX1) || nurl.matches(YT_REGEX2) || nurl.matches(YT_REGEX3);
|
||||
}
|
||||
}
|
||||
|
||||
//Handle button clicks
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton src) {
|
||||
if(browser == null)
|
||||
return;
|
||||
|
||||
if(src.id == 0)
|
||||
browser.goBack();
|
||||
else if(src.id == 1)
|
||||
browser.goForward();
|
||||
else if(src.id == 2) {
|
||||
String fixedURL = ExampleMod.INSTANCE.getAPI().punycode(url.getText());
|
||||
browser.loadURL(fixedURL);
|
||||
} else if(src.id == 3) {
|
||||
ExampleMod.INSTANCE.setBackup(this);
|
||||
mc.displayGuiScreen(null);
|
||||
} else if(src.id == 4) {
|
||||
String loc = browser.getURL();
|
||||
String vId = null;
|
||||
boolean redo = false;
|
||||
|
||||
if(loc.matches(YT_REGEX1))
|
||||
vId = loc.replaceFirst(YT_REGEX1, "$1");
|
||||
else if(loc.matches(YT_REGEX2))
|
||||
vId = loc.replaceFirst(YT_REGEX2, "$1");
|
||||
else if(loc.matches(YT_REGEX3))
|
||||
redo = true;
|
||||
|
||||
if(vId != null || redo) {
|
||||
ExampleMod.INSTANCE.setBackup(this);
|
||||
mc.displayGuiScreen(new ScreenCfg(browser, vId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package net.montoyo.mcef.example;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
|
||||
import net.montoyo.mcef.api.API;
|
||||
import net.montoyo.mcef.api.IBrowser;
|
||||
import net.montoyo.mcef.api.IDisplayHandler;
|
||||
import net.montoyo.mcef.api.IJSQueryCallback;
|
||||
import net.montoyo.mcef.api.IJSQueryHandler;
|
||||
import net.montoyo.mcef.api.MCEFApi;
|
||||
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
/**
|
||||
* An example mod that shows you how to use MCEF.
|
||||
* Assuming that it is client-side only and that onInit() is called on initialization.
|
||||
* This example shows a simple 2D web browser when pressing F6.
|
||||
*
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public class ExampleMod implements IDisplayHandler, IJSQueryHandler {
|
||||
|
||||
public static ExampleMod INSTANCE;
|
||||
|
||||
public ScreenCfg hudBrowser = null;
|
||||
private KeyBinding key = new KeyBinding("Open Browser", Keyboard.KEY_F10, "key.categories.misc");
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
private BrowserScreen backup = null;
|
||||
private API api;
|
||||
|
||||
public API getAPI() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public void onPreInit() {
|
||||
//Grab the API and make sure it isn't null.
|
||||
api = MCEFApi.getAPI();
|
||||
if(api == null)
|
||||
return;
|
||||
|
||||
api.registerScheme("mod", ModScheme.class, true, false, false, true, true, false, false);
|
||||
}
|
||||
|
||||
public void onInit() {
|
||||
INSTANCE = this;
|
||||
|
||||
//Register key binding and listen to the FML event bus for ticks.
|
||||
ClientRegistry.registerKeyBinding(key);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
if(api != null) {
|
||||
//Register this class to handle onAddressChange and onQuery events
|
||||
api.registerDisplayHandler(this);
|
||||
api.registerJSQueryHandler(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBackup(BrowserScreen bu) {
|
||||
backup = bu;
|
||||
}
|
||||
|
||||
public boolean hasBackup() {
|
||||
return (backup != null);
|
||||
}
|
||||
|
||||
public void showScreen(String url) {
|
||||
if(mc.currentScreen instanceof BrowserScreen)
|
||||
((BrowserScreen) mc.currentScreen).loadURL(url);
|
||||
else if(hasBackup()) {
|
||||
mc.displayGuiScreen(backup);
|
||||
backup.loadURL(url);
|
||||
backup = null;
|
||||
} else
|
||||
mc.displayGuiScreen(new BrowserScreen(url));
|
||||
}
|
||||
|
||||
public IBrowser getBrowser() {
|
||||
if(mc.currentScreen instanceof BrowserScreen)
|
||||
return ((BrowserScreen) mc.currentScreen).browser;
|
||||
else if(backup != null)
|
||||
return backup.browser;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTick(TickEvent ev) {
|
||||
if(ev.phase == TickEvent.Phase.START && ev.side == Side.CLIENT && ev.type == TickEvent.Type.CLIENT) {
|
||||
//Check if our key was pressed
|
||||
if(key.isPressed() && !(mc.currentScreen instanceof BrowserScreen)) {
|
||||
//Display the web browser UI.
|
||||
mc.displayGuiScreen(hasBackup() ? backup : new BrowserScreen());
|
||||
backup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddressChange(IBrowser browser, String url) {
|
||||
//Called by MCEF if a browser's URL changes. Forward this event to the screen.
|
||||
if(mc.currentScreen instanceof BrowserScreen)
|
||||
((BrowserScreen) mc.currentScreen).onUrlChanged(browser, url);
|
||||
else if(hasBackup())
|
||||
backup.onUrlChanged(browser, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleChange(IBrowser browser, String title) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTooltip(IBrowser browser, String text) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusMessage(IBrowser browser, String value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleQuery(IBrowser b, long queryId, String query, boolean persistent, IJSQueryCallback cb) {
|
||||
if(b != null && query.equalsIgnoreCase("username")) {
|
||||
if(b.getURL().startsWith("mod://")) {
|
||||
//Only allow MCEF URLs to get the player's username to keep his identity secret
|
||||
|
||||
mc.addScheduledTask(() -> {
|
||||
//Add this to a scheduled task because this is NOT called from the main Minecraft thread...
|
||||
|
||||
try {
|
||||
String name = mc.getSession().getUsername();
|
||||
cb.success(name);
|
||||
} catch(Throwable t) {
|
||||
cb.failure(500, "Internal error.");
|
||||
Log.warning("Could not get username from JavaScript:");
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else
|
||||
cb.failure(403, "Can't access username from external page");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuery(IBrowser b, long queryId) {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onDrawHUD(RenderGameOverlayEvent.Post ev) {
|
||||
if(hudBrowser != null)
|
||||
hudBrowser.drawScreen(0, 0, 0.f);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.montoyo.mcef.example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.api.*;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
public class ModScheme implements IScheme {
|
||||
|
||||
private String contentType = null;
|
||||
private InputStream is = null;
|
||||
|
||||
@Override
|
||||
public SchemePreResponse processRequest(String url) {
|
||||
url = url.substring("mod://".length());
|
||||
|
||||
int pos = url.indexOf('/');
|
||||
if(pos < 0)
|
||||
return SchemePreResponse.NOT_HANDLED;
|
||||
|
||||
String mod = removeSlashes(url.substring(0, pos));
|
||||
String loc = removeSlashes(url.substring(pos + 1));
|
||||
|
||||
if(mod.length() <= 0 || loc.length() <= 0 || mod.charAt(0) == '.' || loc.charAt(0) == '.') {
|
||||
Log.warning("Invalid URL " + url);
|
||||
return SchemePreResponse.NOT_HANDLED;
|
||||
}
|
||||
|
||||
is = ModScheme.class.getResourceAsStream("/assets/" + mod.toLowerCase() + "/html/" + loc.toLowerCase());
|
||||
if(is == null) {
|
||||
Log.warning("Resource " + url + " NOT found!");
|
||||
return SchemePreResponse.NOT_HANDLED; //Mhhhhh... 404?
|
||||
}
|
||||
|
||||
contentType = null;
|
||||
pos = loc.lastIndexOf('.');
|
||||
if(pos >= 0 && pos < loc.length() - 2)
|
||||
contentType = MCEF.PROXY.mimeTypeFromExtension(loc.substring(pos + 1));
|
||||
|
||||
return SchemePreResponse.HANDLED_CONTINUE;
|
||||
}
|
||||
|
||||
private String removeSlashes(String loc) {
|
||||
int i = 0;
|
||||
while(i < loc.length() && loc.charAt(i) == '/')
|
||||
i++;
|
||||
|
||||
return loc.substring(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getResponseHeaders(ISchemeResponseHeaders rep) {
|
||||
if(contentType != null)
|
||||
rep.setMimeType(contentType);
|
||||
|
||||
rep.setStatus(200);
|
||||
rep.setStatusText("OK");
|
||||
rep.setResponseLength(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readResponse(ISchemeResponseData data) {
|
||||
try {
|
||||
int ret = is.read(data.getDataArray(), 0, data.getBytesToRead());
|
||||
if(ret <= 0)
|
||||
is.close();
|
||||
|
||||
data.setAmountRead(Math.max(ret, 0));
|
||||
return ret > 0;
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package net.montoyo.mcef.example;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.montoyo.mcef.api.IBrowser;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ScreenCfg extends GuiScreen {
|
||||
|
||||
private IBrowser browser;
|
||||
private int width = 320;
|
||||
private int height = 180;
|
||||
private int x = 10;
|
||||
private int y = 10;
|
||||
private int offsetX = 0;
|
||||
private int offsetY = 0;
|
||||
private boolean dragging = false;
|
||||
private boolean resizing = false;
|
||||
private boolean drawSquare = true;
|
||||
|
||||
public ScreenCfg(IBrowser b, String vId) {
|
||||
browser = b;
|
||||
if(vId != null)
|
||||
b.loadURL("https://www.youtube.com/embed/" + vId + "?autoplay=1");
|
||||
|
||||
b.resize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInput() {
|
||||
while(Keyboard.next()) {
|
||||
if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
|
||||
drawSquare = false;
|
||||
ExampleMod.INSTANCE.hudBrowser = this;
|
||||
browser.injectMouseMove(-10, -10, 0, true);
|
||||
mc.displayGuiScreen(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while(Mouse.next()) {
|
||||
int btn = Mouse.getEventButton();
|
||||
boolean pressed = Mouse.getEventButtonState();
|
||||
int sx = Mouse.getEventX();
|
||||
int sy = mc.displayHeight - Mouse.getEventY();
|
||||
|
||||
if(btn == 1 && pressed && sx >= x && sy >= y && sx < x + width && sy < y + height) {
|
||||
browser.injectMouseMove(sx - x, sy - y, 0, false);
|
||||
browser.injectMouseButton(sx - x, sy - y, 0, 1, true, 1);
|
||||
browser.injectMouseButton(sx - x, sy - y, 0, 1, false, 1);
|
||||
} else if(dragging) {
|
||||
if(btn == 0 && !pressed)
|
||||
dragging = false;
|
||||
else {
|
||||
x = sx + offsetX;
|
||||
y = sy + offsetY;
|
||||
}
|
||||
} else if(resizing) {
|
||||
if(btn == 0 && !pressed) {
|
||||
resizing = false;
|
||||
browser.resize(width, height);
|
||||
} else {
|
||||
int w = sx - x;
|
||||
int h = sy - y;
|
||||
|
||||
if(w >= 32 && h >= 18) {
|
||||
if(h >= w) {
|
||||
double dw = ((double) h) * (16.0 / 9.0);
|
||||
width = (int) dw;
|
||||
height = h;
|
||||
} else {
|
||||
double dh = ((double) w) * (9.0 / 16.0);
|
||||
width = w;
|
||||
height = (int) dh;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(pressed && btn == 0 && sx >= x && sy >= y && sx < x + width && sy < y + height) { //In browser rect
|
||||
dragging = true;
|
||||
offsetX = x - sx;
|
||||
offsetY = y - sy;
|
||||
} else if(pressed && btn == 0 && sx >= x + width && sy >= y + height && sx < x + width + 10 && sy < y + height + 10) //In resize rect
|
||||
resizing = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int i1, int i2, float f) {
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
browser.draw(unscaleX(x), unscaleY(height + y), unscaleX(width + x), unscaleY(y));
|
||||
|
||||
if(drawSquare) {
|
||||
Tessellator t = Tessellator.getInstance();
|
||||
BufferBuilder vb = t.getBuffer();
|
||||
|
||||
vb.begin(GL11.GL_LINE_LOOP, DefaultVertexFormats.POSITION_COLOR);
|
||||
vb.pos(unscaleX(x + width), unscaleY(y + height), 0.0).color(255, 255, 255, 255).endVertex();
|
||||
vb.pos(unscaleX(x + width + 10), unscaleY(y + height), 0.0).color(255, 255, 255, 255).endVertex();
|
||||
vb.pos(unscaleX(x + width + 10), unscaleY(y + height + 10), 0.0).color(255, 255, 255, 255).endVertex();
|
||||
vb.pos(unscaleX(x + width), unscaleY(y + height + 10), 0.0).color(255, 255, 255, 255).endVertex();
|
||||
t.draw();
|
||||
}
|
||||
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
public double unscaleX(int x) {
|
||||
return ((double) x) / ((double) mc.displayWidth) * ((double) super.width);
|
||||
}
|
||||
|
||||
public double unscaleY(int y) {
|
||||
return ((double) y) / ((double) mc.displayHeight) * ((double) super.height);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package net.montoyo.mcef.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* An object representing an HTTP(S) mirror to download the resources from.
|
||||
*
|
||||
* @author montoyo
|
||||
* @see {@link net.montoyo.mcef.remote.MirrorManager}
|
||||
*/
|
||||
public final class Mirror {
|
||||
|
||||
/**
|
||||
* Whether the mirror is HTTPS or not
|
||||
*/
|
||||
public static final int FLAG_SECURE = 1;
|
||||
|
||||
/**
|
||||
* Whether this mirror has been forced by the user in the MCEF configuration file
|
||||
*/
|
||||
public static final int FLAG_FORCED = 4;
|
||||
|
||||
private final String name;
|
||||
private final String url;
|
||||
private final int flags;
|
||||
|
||||
/**
|
||||
* Constructs a Mirror from its name, URL, and flags.
|
||||
*
|
||||
* @param name The name of the mirror
|
||||
* @param url The corresponding URL
|
||||
* @param flags Its flags
|
||||
*/
|
||||
public Mirror(String name, String url, int flags) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the mirror
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The URL of the mirror
|
||||
*/
|
||||
public String getURL() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The flags of this mirror
|
||||
*/
|
||||
public int getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the secure flag is set
|
||||
* @see #FLAG_SECURE
|
||||
*/
|
||||
public boolean isSecure() {
|
||||
return (flags & FLAG_SECURE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this mirror has been forced by the user
|
||||
* @see #FLAG_FORCED
|
||||
*/
|
||||
public boolean isForced() {
|
||||
return (flags & FLAG_FORCED) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A string informing the user of which mirror was selected
|
||||
*/
|
||||
public String getInformationString() {
|
||||
return isForced() ? ("Mirror location forced by user to: " + url) : ("Selected mirror: " + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a connection to the mirror's resource corresponding to the URL.
|
||||
*
|
||||
* @param name The URL of the resource, relative to the root of the mirror website.
|
||||
* @return A connection to this resource, with timeout set up.
|
||||
* @throws MalformedURLException if the mirror's URL is invalid or if name is invalid.
|
||||
* @throws IOException if an I/O exception occurs.
|
||||
*/
|
||||
public HttpURLConnection getResource(String name) throws MalformedURLException, IOException {
|
||||
HttpURLConnection ret = (HttpURLConnection) (new URL(url + '/' + name)).openConnection();
|
||||
ret.setConnectTimeout(30000);
|
||||
ret.setReadTimeout(15000);
|
||||
ret.setRequestProperty("User-Agent", "MCEF");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.montoyo.mcef.remote;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* MirrorManager keeps track of valid & broken mirrors that should be used to download MCEF resources.
|
||||
* It also makes sure only HTTPS mirrors are used, depending on the user's choices.
|
||||
*
|
||||
* @author montoyo
|
||||
*/
|
||||
public class MirrorManager {
|
||||
|
||||
private static final Mirror[] defaultMirrors = new Mirror[] {
|
||||
// HTTPS mirrors
|
||||
new Mirror("oxmc-mcef-mirror", "https://cdn.oxmc.me/internal/mcef", Mirror.FLAG_SECURE),
|
||||
//new Mirror("ds58-mcef-mirror", "https://ds58-mcef-mirror.ewr1.vultrobjects.com", Mirror.FLAG_SECURE),
|
||||
};
|
||||
|
||||
/**
|
||||
* The unique instance of the MirrorManager
|
||||
*/
|
||||
public static final MirrorManager INSTANCE = new MirrorManager();
|
||||
|
||||
private final ArrayList<Mirror> mirrors = new ArrayList<>();
|
||||
private final Random r = new Random();
|
||||
private Mirror current;
|
||||
|
||||
private MirrorManager() {
|
||||
markCurrentMirrorAsBroken();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mirrors.clear();
|
||||
|
||||
if(MCEF.FORCE_MIRROR != null)
|
||||
mirrors.add(new Mirror("user-forced", MCEF.FORCE_MIRROR, Mirror.FLAG_FORCED));
|
||||
else {
|
||||
ArrayList<Mirror> lst = new ArrayList<>(Arrays.asList(defaultMirrors));
|
||||
|
||||
//Begin by adding all HTTPS mirrors in a random fashion
|
||||
while(!lst.isEmpty()) {
|
||||
Mirror m = lst.remove(r.nextInt(lst.size()));
|
||||
|
||||
if(m.isSecure())
|
||||
mirrors.add(m);
|
||||
}
|
||||
|
||||
//Then add all non-secure mirrors, if user didn't disable them
|
||||
if(!MCEF.SECURE_MIRRORS_ONLY) {
|
||||
lst.addAll(Arrays.asList(defaultMirrors));
|
||||
|
||||
while(!lst.isEmpty()) {
|
||||
Mirror m = lst.remove(r.nextInt(lst.size()));
|
||||
|
||||
if(!m.isSecure())
|
||||
mirrors.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The active mirror to be used
|
||||
*/
|
||||
public Mirror getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the active mirror as broken and chooses another one
|
||||
*
|
||||
* @return false if all mirrors were tested and the list was reset
|
||||
*/
|
||||
public boolean markCurrentMirrorAsBroken() {
|
||||
boolean ret = true;
|
||||
|
||||
if(mirrors.isEmpty()) {
|
||||
reset();
|
||||
ret = false;
|
||||
}
|
||||
|
||||
current = mirrors.remove(0);
|
||||
Log.info(current.getInformationString());
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
package net.montoyo.mcef.remote;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.FileReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.montoyo.mcef.setup.FileListing;
|
||||
import org.cef.OS;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.client.ClientProxy;
|
||||
import net.montoyo.mcef.utilities.IProgressListener;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import net.montoyo.mcef.utilities.Platform;
|
||||
import net.montoyo.mcef.utilities.Util;
|
||||
import net.montoyo.mcef.utilities.Version;
|
||||
import net.montoyo.mcef.utilities.ForgeVersionParser;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import static net.montoyo.mcef.client.ClientProxy.JCEF_ROOT;
|
||||
|
||||
/**
|
||||
* A class for updating and parsing the remote configuration file.
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public class RemoteConfig {
|
||||
|
||||
private static String jcefCommit;
|
||||
private boolean useLegacyJcef = false;
|
||||
private String version = null;
|
||||
private static final String PLATFORM = Platform.getPlatform();
|
||||
private static final String PLATFORMFULL = Platform.getPlatformFull();
|
||||
|
||||
public RemoteConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the MCEF configuration file.
|
||||
*
|
||||
* @param f The configuration file.
|
||||
* @return The parsed configuration file.
|
||||
*/
|
||||
private JsonObject readConfig(File f) {
|
||||
try {
|
||||
return (new JsonParser()).parse(new FileReader(f)).getAsJsonObject();
|
||||
} catch(JsonIOException e) {
|
||||
Log.error("IOException while reading remote config.");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch(FileNotFoundException e) {
|
||||
Log.error("Couldn't find remote config.");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch(Exception e) {
|
||||
Log.error("Syntax error in remote config.");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the MCEF configuration file and parses it.
|
||||
* @return The parsed configuration file.
|
||||
*/
|
||||
private JsonObject readConfig() {
|
||||
File newCfg = new File(JCEF_ROOT, "mcef.new");
|
||||
File cfgFle = new File(JCEF_ROOT, "mcef.json");
|
||||
|
||||
// Get the mirror URL
|
||||
Mirror m = MirrorManager.INSTANCE.getCurrent();
|
||||
String mirrorHost = m.getURL();
|
||||
String cfgUrl = mirrorHost + "/mcef.json";
|
||||
|
||||
boolean ok = Util.downloadFileSafe(cfgUrl, newCfg, null);
|
||||
|
||||
if(ok) {
|
||||
Util.delete(cfgFle);
|
||||
|
||||
if(newCfg.renameTo(cfgFle))
|
||||
return readConfig(cfgFle);
|
||||
else {
|
||||
Log.warning("Couldn't rename mcef.new to mcef.json.");
|
||||
return readConfig(newCfg);
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.warning("Couldn't read remote config. Using local configuration file.");
|
||||
return readConfig(cfgFle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the MCEF configuration file and parses it.
|
||||
* Fills the resources, extract and version fields from it.
|
||||
*/
|
||||
public void load() {
|
||||
JsonObject json = readConfig();
|
||||
if(json == null) {
|
||||
Log.error("Could NOT read either remote and local configuration files. Entering virtual mode.");
|
||||
ClientProxy.VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Log.info("Detected platform: %s", PLATFORM);
|
||||
|
||||
// Check if the config file contains the MCEF version
|
||||
JsonElement ver = json.get(MCEF.VERSION);
|
||||
if(ver == null || !ver.isJsonObject()) {
|
||||
Log.error("Config file does NOT contain this MCEF jar's version (wtf??). Entering virtual mode.");
|
||||
ClientProxy.VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the version data
|
||||
JsonObject vData = ver.getAsJsonObject();
|
||||
|
||||
// Create an instance of ForgeVersionParser
|
||||
ForgeVersionParser forgeVersionParser = new ForgeVersionParser();
|
||||
|
||||
// Get the Minecraft version
|
||||
String mcVersion = forgeVersionParser.parse(Minecraft.getMinecraft().getVersion());
|
||||
|
||||
// Otherwise we fallback to hardcoded version
|
||||
if (mcVersion == null || mcVersion.isEmpty() || mcVersion.contains("forge")) {
|
||||
mcVersion = "1.12.2";
|
||||
}
|
||||
|
||||
// Check for old_versions first
|
||||
JsonElement oldVersions = vData.get("old_versions");
|
||||
if (oldVersions != null && oldVersions.isJsonObject()) {
|
||||
JsonObject oldVersionsObj = oldVersions.getAsJsonObject();
|
||||
|
||||
// Check if the current Minecraft version exists in old_versions
|
||||
if (oldVersionsObj.has(mcVersion)) {
|
||||
JsonElement versionData = oldVersionsObj.get(mcVersion);
|
||||
|
||||
// Parse the options for the current Minecraft version
|
||||
if (versionData != null && versionData.isJsonObject()) {
|
||||
JsonObject versionDataObj = versionData.getAsJsonObject();
|
||||
vData = versionDataObj;
|
||||
|
||||
// Read the "use_legacy_jcef" option, which uses the old JCEF version
|
||||
JsonElement useLegacyJcefOpt = versionDataObj.get("use_legacy_jcef");
|
||||
if (useLegacyJcefOpt != null && useLegacyJcefOpt.isJsonPrimitive()) {
|
||||
this.useLegacyJcef = useLegacyJcefOpt.getAsBoolean();
|
||||
Log.info("Use Legacy JCEF: %s", useLegacyJcef);
|
||||
|
||||
// get the jcef_version object
|
||||
JsonElement jcefVersion = versionDataObj.get("jcef_version");
|
||||
if (jcefVersion != null && jcefVersion.isJsonPrimitive()) {
|
||||
String commitHash = jcefVersion.getAsString();
|
||||
//Log.info("JCEF Commit: %s", commitHash);
|
||||
jcefCommit = commitHash;
|
||||
} else {
|
||||
Log.error("Config file is missing \"jcef_version\" object. Entering virtual mode.");
|
||||
ClientProxy.VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.info("Minecraft version not found in old_versions. Using the latest version.");
|
||||
}
|
||||
}
|
||||
|
||||
// if the "use_legacy_jcef" option is not true we check for git_commit
|
||||
if (!useLegacyJcef) {
|
||||
// Check for the git_commit object
|
||||
JsonElement gitCommit = vData.get("git_commit");
|
||||
if (gitCommit != null && gitCommit.isJsonPrimitive()) {
|
||||
String commitHash = gitCommit.getAsString();
|
||||
//Log.info("Git Commit: %s", commitHash);
|
||||
jcefCommit = commitHash;
|
||||
} else {
|
||||
Log.error("Config file is missing \"git_commit\" object. Entering virtual mode.");
|
||||
ClientProxy.VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for the supported_platforms object
|
||||
JsonElement cat = vData.get("supported_platforms");
|
||||
if(cat == null || !cat.isJsonArray()) {
|
||||
Log.error("Config file is missing \"supported_platforms\" object. Entering virtual mode.");
|
||||
ClientProxy.VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the current platform is supported
|
||||
JsonArray catArr = cat.getAsJsonArray();
|
||||
boolean platformSupported = false;
|
||||
for (JsonElement element : catArr) {
|
||||
if (element.getAsString().equals(PLATFORM)) {
|
||||
platformSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!platformSupported) {
|
||||
Log.error("Your platform isn't supported by MCEF yet. Entering virtual mode.");
|
||||
ClientProxy.VIRTUAL = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for the latestVersions object
|
||||
JsonElement mcVersions = json.get("latestVersions");
|
||||
if(mcVersions != null && mcVersions.isJsonObject()) {
|
||||
JsonElement cVer = mcVersions.getAsJsonObject().get(mcVersion);
|
||||
|
||||
if(cVer != null && cVer.isJsonPrimitive())
|
||||
version = cVer.getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects missing files, download them, and extracts them.
|
||||
*
|
||||
* @param ipl The progress listener.
|
||||
* @return true if the operation was successful.
|
||||
*/
|
||||
public boolean downloadMCEF(IProgressListener ipl) {
|
||||
Mirror m = MirrorManager.INSTANCE.getCurrent();
|
||||
String mirrorHost = m.getURL();
|
||||
String baseURL;
|
||||
if (this.useLegacyJcef) {
|
||||
baseURL = mirrorHost+"/legacy/"+jcefCommit+"/";
|
||||
} else {
|
||||
baseURL = mirrorHost+"/java-cef-builds/"+jcefCommit+"/";
|
||||
}
|
||||
String targz = PLATFORMFULL+".tar.gz";
|
||||
//https://mcef-download.cinemamod.com/java-cef-builds/eaeb3d4370aa3526ee237ad1981ad59af3de4dd1/windows_amd64.tar.gz
|
||||
// or
|
||||
//https://mcef-download.cinemamod.com/legacy/1.33/windows_amd64.tar.gz
|
||||
String JAVA_CEF_DOWNLOAD_URL = baseURL+targz;
|
||||
String JAVA_CEF_CHECKSUM_DOWNLOAD_URL = baseURL+targz+".sha256";
|
||||
boolean shouldDownload = false;
|
||||
try {
|
||||
ipl = Util.secure(ipl);
|
||||
|
||||
// Prepare the file paths and URLs
|
||||
File mcefLibrariesPath = new File(JCEF_ROOT);
|
||||
File cefRuntimeFolder = new File(mcefLibrariesPath, "cef_runtime");
|
||||
String downloadUrl = JAVA_CEF_DOWNLOAD_URL;
|
||||
String checksumUrl = JAVA_CEF_CHECKSUM_DOWNLOAD_URL;
|
||||
|
||||
File tarGzFile = new File(mcefLibrariesPath, PLATFORMFULL + ".tar.gz");
|
||||
File checksumFile = new File(mcefLibrariesPath, PLATFORMFULL + ".tar.gz.sha256");
|
||||
File checksumTempFile = new File(mcefLibrariesPath, PLATFORMFULL + ".tar.gz.sha256.temp");
|
||||
|
||||
// Log the file paths
|
||||
//Log.info("JCEF libraries path: %s", mcefLibrariesPath.getAbsolutePath());
|
||||
//Log.info("JCEF tar.gz file: %s", tarGzFile.getAbsolutePath());
|
||||
//Log.info("JCEF checksum file: %s", checksumFile.getAbsolutePath());
|
||||
//Log.info("JCEF checksum temp file: %s", checksumTempFile.getAbsolutePath());
|
||||
|
||||
// Download the checksum file
|
||||
Util.downloadFile(checksumUrl, checksumTempFile, ipl);
|
||||
|
||||
// Step 3: Verify checksum
|
||||
if (checksumFile.exists()) {
|
||||
boolean sameContent = FileUtils.contentEquals(checksumFile, checksumTempFile);
|
||||
if (sameContent) {
|
||||
// Before we delete the temp file, we need to check if the JCEF build is already downloaded
|
||||
if (cefRuntimeFolder.exists()) {
|
||||
Log.info("JCEF build already downloaded, Skipping download.");
|
||||
checksumTempFile.delete();
|
||||
shouldDownload = false;
|
||||
} else {
|
||||
Log.info("JCEF build not downloaded yet and or missing, Downloading JCEF build.");
|
||||
checksumTempFile.renameTo(checksumFile);
|
||||
shouldDownload = true;
|
||||
}
|
||||
} else {
|
||||
Log.warning("Checksum file doesn't match. Redownloading JCEF build.");
|
||||
checksumTempFile.renameTo(checksumFile);
|
||||
shouldDownload = true;
|
||||
}
|
||||
} else {
|
||||
// If the checksum file doesn't exist, then we need to download the JCEF build
|
||||
shouldDownload = true;
|
||||
checksumTempFile.renameTo(checksumFile);
|
||||
}
|
||||
|
||||
// Step 4: Download the JCEF build if necessary
|
||||
if (shouldDownload && !MCEF.SKIP_UPDATES) {
|
||||
// Download the JCEF build
|
||||
Util.downloadFile(downloadUrl, tarGzFile, ipl);
|
||||
|
||||
// Extract the tar.gz file
|
||||
Util.extractTarGz(tarGzFile, mcefLibrariesPath, ipl);
|
||||
|
||||
// Define extracted folder (platform_arch) and target folder (cef_runtime)
|
||||
File extractedFolder = new File(mcefLibrariesPath, PLATFORMFULL);
|
||||
|
||||
// Ensure the old cef_runtime folder is removed before renaming
|
||||
if (cefRuntimeFolder.exists()) {
|
||||
FileUtils.deleteDirectory(cefRuntimeFolder);
|
||||
}
|
||||
|
||||
// Rename extracted folder to cef_runtime
|
||||
if (extractedFolder.exists()) {
|
||||
boolean renamed = extractedFolder.renameTo(cefRuntimeFolder);
|
||||
if (!renamed) {
|
||||
Log.warning("Failed to rename extracted folder to cef_runtime");
|
||||
}
|
||||
} else {
|
||||
Log.warning("Extracted folder does not exist, rename skipped.");
|
||||
}
|
||||
|
||||
// Delete the tar.gz file
|
||||
if (tarGzFile.exists()) {
|
||||
tarGzFile.delete();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.error("Failed to download or extract JCEF build: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an info string if an MCEF update is available.
|
||||
* @return an info string if a newer version exists, null otherwise.
|
||||
*/
|
||||
public String getUpdateString() {
|
||||
if(version == null)
|
||||
return null;
|
||||
|
||||
Version cur = new Version(MCEF.VERSION);
|
||||
Version cfg = new Version(version);
|
||||
|
||||
if(cfg.isBiggerThan(cur))
|
||||
return "New MCEF version available. Current: " + cur + ", latest: " + cfg + '.';
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CfgParser {
|
||||
|
||||
private static abstract class Line {
|
||||
|
||||
public abstract void write(BufferedWriter bw) throws IOException;
|
||||
public abstract void read(String content, Matcher m);
|
||||
|
||||
}
|
||||
|
||||
private static class CommentLine extends Line {
|
||||
|
||||
private String data;
|
||||
|
||||
public CommentLine(String d) {
|
||||
data = d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(BufferedWriter bw) throws IOException {
|
||||
bw.write(data + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String content, Matcher m) {
|
||||
data = content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class BeginCategoryLine extends Line {
|
||||
|
||||
public static final String REGEX = "^(\\s*)([a-z]+)(\\s+)\\{(\\s*)$";
|
||||
private String prefix;
|
||||
private String category;
|
||||
private String inBetween;
|
||||
private String suffix;
|
||||
|
||||
public BeginCategoryLine() {
|
||||
}
|
||||
|
||||
public BeginCategoryLine(String name) {
|
||||
prefix = "";
|
||||
category = name;
|
||||
inBetween = " ";
|
||||
suffix = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(BufferedWriter bw) throws IOException {
|
||||
bw.write(prefix);
|
||||
bw.write(category);
|
||||
bw.write(inBetween);
|
||||
bw.write("{");
|
||||
bw.write(suffix + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String content, Matcher m) {
|
||||
prefix = m.group(1);
|
||||
category = m.group(2);
|
||||
inBetween = m.group(3);
|
||||
suffix = m.group(4);
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
return category;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class EndCategoryLine extends Line {
|
||||
|
||||
public static final String REGEX = "^(\\s*)\\}(\\s*)$";
|
||||
private String prefix;
|
||||
private String suffix;
|
||||
|
||||
public EndCategoryLine() {
|
||||
prefix = "";
|
||||
suffix = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(BufferedWriter bw) throws IOException {
|
||||
bw.write(prefix);
|
||||
bw.write("}");
|
||||
bw.write(suffix + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String content, Matcher m) {
|
||||
prefix = m.group(1);
|
||||
suffix = m.group(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PropertyLine extends Line {
|
||||
|
||||
public static final String REGEX = "^(\\s*)([A-Z])\\:([A-Za-z]+)=(.*)$";
|
||||
private String prefix;
|
||||
private char type;
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
public PropertyLine() {
|
||||
}
|
||||
|
||||
public PropertyLine(char t, String key, String val) {
|
||||
prefix = " ";
|
||||
type = t;
|
||||
this.key = key;
|
||||
value = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(BufferedWriter bw) throws IOException {
|
||||
bw.write(prefix);
|
||||
bw.write(type + ":" + key);
|
||||
bw.write("=" + value + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(String content, Matcher m) {
|
||||
prefix = m.group(1);
|
||||
type = m.group(2).charAt(0);
|
||||
key = m.group(3);
|
||||
value = m.group(4);
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean getBooleanValue(boolean def) {
|
||||
String data = value.trim().toLowerCase();
|
||||
|
||||
if(data.equals("false"))
|
||||
return false;
|
||||
else if(data.equals("true"))
|
||||
return true;
|
||||
else {
|
||||
value = def ? "true" : "false";
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(String v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum LineType {
|
||||
|
||||
CATEGORY_BEGIN(BeginCategoryLine.REGEX, BeginCategoryLine.class),
|
||||
CATEGORY_END(EndCategoryLine.REGEX, EndCategoryLine.class),
|
||||
PROPERTY(PropertyLine.REGEX, PropertyLine.class);
|
||||
|
||||
private final Pattern pattern;
|
||||
private final Class<? extends Line> cls;
|
||||
|
||||
LineType(String regex, Class<? extends Line> cls) {
|
||||
pattern = Pattern.compile(regex);
|
||||
this.cls = cls;
|
||||
}
|
||||
|
||||
public static Line parseLine(String l) {
|
||||
for(LineType lt : values()) {
|
||||
Matcher m = lt.pattern.matcher(l);
|
||||
|
||||
if(m.matches()) {
|
||||
try {
|
||||
Line ret = lt.cls.newInstance();
|
||||
ret.read(l, m);
|
||||
|
||||
return ret;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could not instantiate line class \"" + lt.cls.getCanonicalName() + "\":");
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ArrayList<Line> lines = new ArrayList<Line>();
|
||||
private HashMap<String, HashMap<String, PropertyLine>> data = new HashMap<String, HashMap<String, PropertyLine>>();
|
||||
private File location;
|
||||
|
||||
public CfgParser(File loc) {
|
||||
location = loc;
|
||||
}
|
||||
|
||||
public boolean load() {
|
||||
try {
|
||||
unsafeRead();
|
||||
return true;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could not read config file \"" + location.getAbsolutePath() + "\":");
|
||||
t.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void unsafeRead() throws Throwable {
|
||||
lines.clear();
|
||||
data.clear();
|
||||
|
||||
BufferedReader br = new BufferedReader(new FileReader(location));
|
||||
String line;
|
||||
String currentCategory = null;
|
||||
int lineCnt = 0;
|
||||
|
||||
while((line = br.readLine()) != null) {
|
||||
String trimmed = line.trim();
|
||||
Line l;
|
||||
|
||||
if(trimmed.isEmpty() || trimmed.charAt(0) == '#')
|
||||
l = new CommentLine(line);
|
||||
else
|
||||
l = LineType.parseLine(line);
|
||||
|
||||
if(l == null)
|
||||
throw new RuntimeException("Could not parse line #" + (lineCnt + 1) + ".");
|
||||
|
||||
if(l instanceof BeginCategoryLine) {
|
||||
if(currentCategory == null) {
|
||||
currentCategory = ((BeginCategoryLine) l).getCategoryName();
|
||||
data.put(currentCategory, new HashMap<String, PropertyLine>());
|
||||
} else
|
||||
throw new RuntimeException("At line #" + (lineCnt + 1) + ": Forgot to close brackets.");
|
||||
} else if(l instanceof EndCategoryLine) {
|
||||
if(currentCategory == null)
|
||||
throw new RuntimeException("At line #" + (lineCnt + 1) + ": Closing non-opened bracket.");
|
||||
else
|
||||
currentCategory = null;
|
||||
} else if(l instanceof PropertyLine) {
|
||||
if(currentCategory == null)
|
||||
throw new RuntimeException("At line #" + (lineCnt + 1) + ": Setting property outside brackets.");
|
||||
else
|
||||
data.get(currentCategory).put(((PropertyLine) l).getKey(), (PropertyLine) l);
|
||||
}
|
||||
|
||||
lines.add(l);
|
||||
lineCnt++;
|
||||
}
|
||||
|
||||
SetupUtil.silentClose(br);
|
||||
}
|
||||
|
||||
public boolean save() {
|
||||
try {
|
||||
unsafeWrite();
|
||||
return true;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could not write config file \"" + location.getAbsolutePath() + "\":");
|
||||
t.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void unsafeWrite() throws Throwable {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(location));
|
||||
for(Line l : lines)
|
||||
l.write(bw);
|
||||
|
||||
SetupUtil.silentClose(bw);
|
||||
}
|
||||
|
||||
private int findCategoryBeginning(String cat) {
|
||||
for(int i = 0; i < lines.size(); i++) {
|
||||
Line l = lines.get(i);
|
||||
|
||||
if(l instanceof BeginCategoryLine && ((BeginCategoryLine) l).getCategoryName().equals(cat))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private PropertyLine getValue(char type, String category, String key, String def) {
|
||||
HashMap<String, PropertyLine> subdata;
|
||||
|
||||
if(data.containsKey(category)) {
|
||||
subdata = data.get(category);
|
||||
|
||||
if(subdata.containsKey(key))
|
||||
return subdata.get(key);
|
||||
else {
|
||||
int pos = findCategoryBeginning(category);
|
||||
if(pos < 0)
|
||||
throw new RuntimeException("Could not find beginning for category \"" + category + "\"! This should NOT happen!");
|
||||
|
||||
PropertyLine pl = new PropertyLine(type, key, def);
|
||||
lines.add(pos + 1, pl);
|
||||
subdata.put(key, pl);
|
||||
return pl;
|
||||
}
|
||||
} else {
|
||||
BeginCategoryLine bcl = new BeginCategoryLine(category);
|
||||
PropertyLine pl = new PropertyLine(type, key, def);
|
||||
EndCategoryLine ecl = new EndCategoryLine();
|
||||
|
||||
lines.add(bcl);
|
||||
lines.add(pl);
|
||||
lines.add(ecl);
|
||||
|
||||
subdata = new HashMap<String, PropertyLine>();
|
||||
subdata.put(key, pl);
|
||||
data.put(category, subdata);
|
||||
return pl;
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringValue(String category, String key, String def) {
|
||||
return getValue('S', category, key, def).getValue();
|
||||
}
|
||||
|
||||
public boolean getBooleanValue(String category, String key, boolean def) {
|
||||
return getValue('B', category, key, def ? "true" : "false").getBooleanValue(def);
|
||||
}
|
||||
|
||||
public void setStringValue(String category, String key, String val) {
|
||||
getValue('S', category, key, val).setValue(val);
|
||||
}
|
||||
|
||||
public void setBooleanValue(String category, String key, boolean val) {
|
||||
String data = val ? "true" : "false";
|
||||
getValue('B', category, key, data).setValue(data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.io.File;
|
||||
|
||||
public class ConfigForm implements ActionListener, WindowListener {
|
||||
|
||||
private CfgParser config;
|
||||
private JFrame parent;
|
||||
private JFrame frame;
|
||||
private JPanel contentPane;
|
||||
|
||||
//Main
|
||||
private JPanel cMainPane;
|
||||
private JTextField cMainForcedMirror;
|
||||
private JCheckBox cMainSkipUpdates;
|
||||
private JCheckBox cMainForgeSplash;
|
||||
private JCheckBox cMainWarnUpdates;
|
||||
|
||||
//Browser
|
||||
private JPanel cBrowserPane;
|
||||
private JCheckBox cBrowserEnable;
|
||||
private JTextField cBrowserHome;
|
||||
|
||||
//Buttons
|
||||
private JPanel btnPane;
|
||||
private JButton btnOk;
|
||||
private JButton btnBack;
|
||||
private JButton btnApply;
|
||||
|
||||
public ConfigForm(JFrame p, File cfgFile) {
|
||||
GridBagConstraints c;
|
||||
config = new CfgParser(cfgFile);
|
||||
parent = p;
|
||||
|
||||
config.load();
|
||||
|
||||
//Frame and content pane
|
||||
frame = new JFrame("MCEF Setup - Configuration");
|
||||
frame.setMinimumSize(new Dimension(500, 1));
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
frame.addWindowListener(this);
|
||||
|
||||
contentPane = new JPanel();
|
||||
contentPane.setBorder(new EmptyBorder(3, 3, 3, 3));
|
||||
contentPane.setLayout(new GridBagLayout());
|
||||
|
||||
//Main
|
||||
cMainPane = new JPanel();
|
||||
cMainPane.setBorder(new TitledBorder("Main"));
|
||||
cMainPane.setLayout(new GridBagLayout());
|
||||
|
||||
cMainForcedMirror = new JTextField();
|
||||
cMainSkipUpdates = new JCheckBox();
|
||||
cMainForgeSplash = new JCheckBox();
|
||||
cMainWarnUpdates = new JCheckBox();
|
||||
addFormComponent(cMainPane, 0, "Forced mirror", cMainForcedMirror);
|
||||
addFormComponent(cMainPane, 1, "Skip updates", cMainSkipUpdates);
|
||||
addFormComponent(cMainPane, 2, "Use forge splash", cMainForgeSplash);
|
||||
addFormComponent(cMainPane, 3, "Warn updates", cMainWarnUpdates);
|
||||
|
||||
cMainForcedMirror.setText(config.getStringValue("main", "forcedMirror", ""));
|
||||
cMainSkipUpdates.setSelected(config.getBooleanValue("main", "skipUpdates", false));
|
||||
cMainForgeSplash.setSelected(config.getBooleanValue("main", "useForgeSplash", true));
|
||||
cMainWarnUpdates.setSelected(config.getBooleanValue("main", "warnUpdates", true));
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridy = 4;
|
||||
c.fill = GridBagConstraints.VERTICAL;
|
||||
c.weighty = 1.0;
|
||||
cMainPane.add(Box.createVerticalGlue(), c);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.weightx = 1.0;
|
||||
c.weighty = 0.5;
|
||||
contentPane.add(cMainPane, c);
|
||||
|
||||
//Browser
|
||||
cBrowserPane = new JPanel();
|
||||
cBrowserPane.setBorder(new TitledBorder("Browser"));
|
||||
cBrowserPane.setLayout(new GridBagLayout());
|
||||
|
||||
cBrowserEnable = new JCheckBox();
|
||||
cBrowserHome = new JTextField();
|
||||
addFormComponent(cBrowserPane, 0, "Enable", cBrowserEnable);
|
||||
addFormComponent(cBrowserPane, 1, "Home page", cBrowserHome);
|
||||
|
||||
cBrowserEnable.setSelected(config.getBooleanValue("examplebrowser", "enable", true));
|
||||
cBrowserHome.setText(config.getStringValue("examplebrowser", "home", "mod://mcef/home.html"));
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridy = 2;
|
||||
c.fill = GridBagConstraints.VERTICAL;
|
||||
c.weighty = 1.0;
|
||||
cBrowserPane.add(Box.createVerticalGlue(), c);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridy = 1;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.weightx = 1.0;
|
||||
c.weighty = 0.5;
|
||||
contentPane.add(cBrowserPane, c);
|
||||
|
||||
//Buttons
|
||||
btnPane = new JPanel();
|
||||
btnPane.setLayout(new GridBagLayout());
|
||||
|
||||
btnOk = new JButton("Ok");
|
||||
btnBack = new JButton("Back");
|
||||
btnApply = new JButton("Apply");
|
||||
btnOk.addActionListener(this);
|
||||
btnBack.addActionListener(this);
|
||||
btnApply.addActionListener(this);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
btnPane.add(Box.createHorizontalGlue(), c);
|
||||
|
||||
addFormButton(1, btnOk);
|
||||
addFormButton(2, btnBack);
|
||||
addFormButton(3, btnApply);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridy = 2;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
contentPane.add(btnPane, c);
|
||||
|
||||
//Display
|
||||
frame.setContentPane(contentPane);
|
||||
frame.pack();
|
||||
parent.setVisible(false);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private void addFormComponent(JPanel pane, int line, String label, Component comp) {
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.insets = new Insets(0, 3, 3, 3);
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridy = line;
|
||||
pane.add(new JLabel(label), c);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.insets = new Insets(0, 0, 3, 3);
|
||||
c.gridx = 1;
|
||||
c.gridy = line;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
pane.add(comp, c);
|
||||
}
|
||||
|
||||
private void addFormButton(int x, JButton btn) {
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.insets = new Insets(3, 0, 3, 3);
|
||||
c.gridx = x;
|
||||
btnPane.add(btn, c);
|
||||
}
|
||||
|
||||
private void saveChanges() {
|
||||
config.setStringValue("main", "forcedMirror", cMainForcedMirror.getText());
|
||||
config.setBooleanValue("main", "skipUpdates", cMainSkipUpdates.isSelected());
|
||||
config.setBooleanValue("main", "useForgeSplash", cMainForgeSplash.isSelected());
|
||||
config.setBooleanValue("main", "warnUpdates", cMainWarnUpdates.isSelected());
|
||||
config.setBooleanValue("examplebrowser", "enable", cBrowserEnable.isSelected());
|
||||
config.setStringValue("examplebrowser", "home", cBrowserHome.getText());
|
||||
|
||||
if(!config.save())
|
||||
JOptionPane.showMessageDialog(frame, "Could not save configuration file.\nMake sure you have the permissions to write in the config folder.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if(e.getSource() == btnBack)
|
||||
windowClosing(null);
|
||||
else if(e.getSource() == btnApply)
|
||||
saveChanges();
|
||||
else if(e.getSource() == btnOk) {
|
||||
saveChanges();
|
||||
windowClosing(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
parent.setVisible(true);
|
||||
frame.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowIconified(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeiconified(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowActivated(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeactivated(WindowEvent e) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
final class DefaultComparator implements Comparator<String> {
|
||||
|
||||
@Override
|
||||
public int compare(String a, String b) {
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o != null && o instanceof DefaultComparator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* This class will be extracted in another temporary JAR in order to self-destroy the MCEF jar after exit.
|
||||
* It has to be standalone and must remain as light as possible for the user's convenience.
|
||||
*/
|
||||
public class Deleter {
|
||||
|
||||
//Sorry about the copy/paste, but this class needs to be standalone!
|
||||
private static boolean tryDelete(File f) {
|
||||
if(!f.exists())
|
||||
return true;
|
||||
|
||||
if(f.delete())
|
||||
return true;
|
||||
else {
|
||||
File dst = new File(f.getParentFile(), f.getName() + "_" + System.currentTimeMillis() % ((int) (Math.random() * 10000)) + ".tmp");
|
||||
|
||||
if(f.renameTo(dst)) {
|
||||
if(!dst.delete())
|
||||
dst.deleteOnExit();
|
||||
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
File f = new File(args[0]);
|
||||
String lowerName = f.getName().toLowerCase();
|
||||
|
||||
if(lowerName.startsWith("mcef") && lowerName.endsWith(".jar")) {
|
||||
for(int i = 0; i < 30; i++) {
|
||||
if(tryDelete(f))
|
||||
return;
|
||||
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch(Throwable t) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class FileListing {
|
||||
|
||||
private ArrayList<String> fileNames = new ArrayList<String>();
|
||||
private File location;
|
||||
|
||||
public FileListing(File dir) {
|
||||
location = new File(dir, "mcefFiles.lst");
|
||||
|
||||
if(location.exists())
|
||||
load();
|
||||
}
|
||||
|
||||
public boolean load() {
|
||||
try {
|
||||
unsafeLoad();
|
||||
return true;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Coud not read file listing:");
|
||||
t.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void unsafeLoad() throws Throwable {
|
||||
BufferedReader br = new BufferedReader(new FileReader(location));
|
||||
String line;
|
||||
|
||||
fileNames.clear();
|
||||
|
||||
while((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
|
||||
if(line.length() > 0 && line.charAt(0) != '#' && line.charAt(0) != '.' && line.charAt(0) != '/' && line.charAt(0) != '\\')
|
||||
fileNames.add(line);
|
||||
}
|
||||
|
||||
SetupUtil.silentClose(br);
|
||||
}
|
||||
|
||||
public boolean save() {
|
||||
try {
|
||||
unsafeSave();
|
||||
return true;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Coud not write file listing:");
|
||||
t.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void unsafeSave() throws Throwable {
|
||||
if(location.exists())
|
||||
SetupUtil.tryDelete(location);
|
||||
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(location));
|
||||
bw.write("# DO NOT EDIT THIS FILE. IT HAS BEEN AUTOMATICALLY GENERATED.\n");
|
||||
bw.write("# This file contains the list of files installed by MCEF.\n");
|
||||
bw.write("# If you remove MCEF, they are no longer needed and you can safely remove them,\n");
|
||||
bw.write("# or you can let the uninstaller do it for you. Just run the MCEF mod jar using Java.\n\n");
|
||||
|
||||
for(String f : fileNames)
|
||||
bw.write(f + "\n");
|
||||
|
||||
SetupUtil.silentClose(bw);
|
||||
}
|
||||
|
||||
public void addFile(String f) {
|
||||
if(!fileNames.contains(f))
|
||||
fileNames.add(f);
|
||||
}
|
||||
|
||||
public boolean addZip(String fname) {
|
||||
try {
|
||||
addZipUnsafe(fname);
|
||||
return true;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Coud not list file in ZIP archive \"" + fname + "\":");
|
||||
t.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void addZipUnsafe(String fname) throws Throwable {
|
||||
ArrayList<String> files = new ArrayList<String>();
|
||||
ZipInputStream zis = new ZipInputStream(new FileInputStream(fname));
|
||||
ZipEntry ze;
|
||||
|
||||
while((ze = zis.getNextEntry()) != null) {
|
||||
String name = ze.getName();
|
||||
|
||||
if(ze.isDirectory() && (name.endsWith("/") || name.endsWith("\\")))
|
||||
files.add(name.substring(0, name.length() - 1));
|
||||
else
|
||||
files.add(name);
|
||||
}
|
||||
|
||||
SetupUtil.silentClose(zis);
|
||||
|
||||
files.sort(new SlashComparator(new DefaultComparator()));
|
||||
for(String t: files)
|
||||
addFile(t); //Use addFile instead of fileNames.addAll() to remove duplicates
|
||||
}
|
||||
|
||||
public Iterator<String> iterator() {
|
||||
return fileNames.iterator();
|
||||
}
|
||||
|
||||
public boolean selfDestruct() {
|
||||
return SetupUtil.tryDelete(location);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.io.File;
|
||||
|
||||
public class McLocationPrompt implements ActionListener, WindowListener {
|
||||
|
||||
private JFrame parent;
|
||||
private JFrame frame;
|
||||
private GridLayout layout;
|
||||
private JPanel mainPane;
|
||||
private JTextField locationField;
|
||||
private JButton btnLocate;
|
||||
private JButton btnBack;
|
||||
private JButton btnOk;
|
||||
private String action;
|
||||
|
||||
public McLocationPrompt(JFrame p, String action) {
|
||||
parent = p;
|
||||
this.action = action;
|
||||
|
||||
//Setup
|
||||
frame = new JFrame("MCEF Setup - Minecraft location");
|
||||
frame.setMinimumSize(new Dimension(500, 1));
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
frame.addWindowListener(this);
|
||||
|
||||
mainPane = new JPanel();
|
||||
layout = new GridLayout(3, 1, 3, 3);
|
||||
mainPane.setBorder(new EmptyBorder(3, 3, 3, 3));
|
||||
mainPane.setLayout(layout);
|
||||
|
||||
//First line: label
|
||||
mainPane.add(new JLabel("Please tell us where Minecraft is installed:"));
|
||||
|
||||
//Second line: field location, locate button
|
||||
JPanel line = new JPanel(new GridBagLayout());
|
||||
line.setMinimumSize(new Dimension(1, 250));
|
||||
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.weightx = 1.0;
|
||||
c.weighty = 1.0;
|
||||
c.insets = new Insets(0, 0, 0, 3);
|
||||
line.add(locationField = new JTextField(), c);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.VERTICAL;
|
||||
c.gridx = 1;
|
||||
c.weighty = 1.0;
|
||||
line.add(btnLocate = new JButton("..."), c);
|
||||
btnLocate.addActionListener(this);
|
||||
mainPane.add(line);
|
||||
|
||||
//Third line: gap, back, ok
|
||||
line = new JPanel(new GridBagLayout());
|
||||
c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
line.add(Box.createHorizontalGlue(), c);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 1;
|
||||
c.insets = new Insets(0, 0, 0, 3);
|
||||
line.add(btnBack = new JButton("Back"), c);
|
||||
btnBack.addActionListener(this);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 2;
|
||||
line.add(btnOk = new JButton("Ok"), c);
|
||||
btnOk.addActionListener(this);
|
||||
mainPane.add(line);
|
||||
|
||||
//Fill location field
|
||||
try {
|
||||
locationField.setText(autoLocateMinecraft());
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Note: could not locate Minecraft:");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
//Display
|
||||
frame.setContentPane(mainPane);
|
||||
frame.pack();
|
||||
parent.setVisible(false);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private String autoLocateMinecraft() {
|
||||
File cDir = (new File(".")).getAbsoluteFile();
|
||||
if(cDir.getName().equals("mods")) {
|
||||
File pFile = cDir.getParentFile();
|
||||
File saves = new File(pFile, "saves");
|
||||
File rpacks = new File(pFile, "resourcepacks");
|
||||
|
||||
if(saves.exists() && saves.isDirectory() && rpacks.exists() && rpacks.isDirectory())
|
||||
return pFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
File root = new File(System.getProperty("user.home", "."));
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if(os.contains("win"))
|
||||
root = new File(System.getenv("APPDATA"));
|
||||
else if(os.contains("mac"))
|
||||
root = new File(new File(root, "Library"), "Application Support");
|
||||
|
||||
root = new File(root, ".minecraft");
|
||||
return root.exists() ? root.getAbsolutePath() : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if(e.getSource() == btnLocate) {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setDialogTitle("Where's Minecraft?");
|
||||
fc.setCurrentDirectory(new File(locationField.getText()));
|
||||
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
|
||||
if(fc.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION)
|
||||
locationField.setText(fc.getSelectedFile().getAbsolutePath());
|
||||
} else if(e.getSource() == btnBack) {
|
||||
parent.setVisible(true);
|
||||
frame.dispose();
|
||||
} else if(e.getSource() == btnOk) {
|
||||
File loc = new File(locationField.getText());
|
||||
if(!loc.exists() || !loc.isDirectory()) {
|
||||
JOptionPane.showMessageDialog(frame, "The selected directory does not exist.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
File saves = new File(loc, "saves");
|
||||
File rpacks = new File(loc, "resourcepacks");
|
||||
|
||||
if(!saves.exists() || !saves.isDirectory() || !rpacks.exists() || !rpacks.isDirectory()) {
|
||||
if(JOptionPane.showConfirmDialog(frame, "The selected directory does not look like a valid Minecraft setup...\nWould you like to continue?", "Hmmm...", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION)
|
||||
return;
|
||||
}
|
||||
|
||||
if(action.equals("configure")) {
|
||||
File configDir = new File(loc, "config");
|
||||
if(!configDir.exists())
|
||||
configDir.mkdirs();
|
||||
|
||||
new ConfigForm(parent, new File(configDir, "MCEF.cfg"));
|
||||
frame.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if(((Boolean) Processes.class.getMethod(action, JFrame.class, File.class).invoke(null, frame, loc)).booleanValue()) {
|
||||
parent.setVisible(true);
|
||||
frame.dispose();
|
||||
}
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could not execute action \"" + action + "\":");
|
||||
t.printStackTrace();
|
||||
JOptionPane.showMessageDialog(frame, "Could not execute action \"" + action + "\".\nThis shouldn't happen; please contact mod author.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
parent.setVisible(true);
|
||||
frame.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowIconified(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeiconified(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowActivated(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeactivated(WindowEvent e) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class Processes {
|
||||
|
||||
public static boolean install(JFrame parent, File dst) {
|
||||
File curJar = SetupUtil.getSelfJarLocation();
|
||||
if(curJar == null) {
|
||||
JOptionPane.showMessageDialog(parent, "Could not locate the current JAR file.\nThis shouldn't happen, contact mod author.\nCannot continue.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
File mods = new File(dst, "mods");
|
||||
if(mods.exists()) {
|
||||
File[] modList = mods.listFiles();
|
||||
|
||||
for(File f: modList) {
|
||||
String fname = f.getName().toLowerCase();
|
||||
|
||||
if(SetupUtil.areFileEqual(f, curJar)) {
|
||||
SetupUI.INSTANCE.abortSelfDestruct();
|
||||
JOptionPane.showMessageDialog(parent, "MCEF was successfully installed!\nIn fact, it was already installed here.\nAlso make sure Forge is installed!", "Well... there was nothing to do!", JOptionPane.INFORMATION_MESSAGE);
|
||||
return true;
|
||||
} else if(f.isFile() && fname.startsWith("mcef") && fname.endsWith(".jar")) {
|
||||
while(!SetupUtil.tryDelete(f)) {
|
||||
if(JOptionPane.showConfirmDialog(parent, "An older version of MCEF has been found and cannot be deleted.\nPlease close Minecraft or remove the following file manually:\n" + f.getAbsolutePath(), "WARNING", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(!mods.mkdir()) {
|
||||
JOptionPane.showMessageDialog(parent, "Could not create mods directory. Make sure you have the rights to do that.\nCannot continue.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(SetupUtil.copyFile(curJar, new File(mods, curJar.getName()))) {
|
||||
JOptionPane.showMessageDialog(parent, "MCEF was successfully installed!\nDon't forget to install Forge!", "All done!", JOptionPane.INFORMATION_MESSAGE);
|
||||
return true;
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(parent, "Installation failed\nCould not copy JAR to mods folder.", "Critical error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean recursiveDelete(File dir) {
|
||||
if(!dir.exists())
|
||||
return true;
|
||||
|
||||
File[] files = dir.listFiles();
|
||||
boolean allOk = true;
|
||||
|
||||
for(File f: files) {
|
||||
if(f.isDirectory()) {
|
||||
if(!recursiveDelete(f))
|
||||
allOk = false;
|
||||
} else if(!SetupUtil.tryDelete(f))
|
||||
allOk = false;
|
||||
}
|
||||
|
||||
return SetupUtil.tryDelete(dir) && allOk;
|
||||
}
|
||||
|
||||
public static boolean uninstall(JFrame parent, File dst) {
|
||||
File configDir = new File(dst, "config");
|
||||
FileListing fl = new FileListing(configDir);
|
||||
if(!fl.load()) {
|
||||
JOptionPane.showMessageDialog(parent, "Could not locate MCEF file listing. It is either missing,\nor you selected the wrong Minecraft location.\nCannot continue.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Destroy resources and cache
|
||||
boolean allDeleted = true;
|
||||
Iterator<String> files = fl.iterator();
|
||||
|
||||
while(files.hasNext()) {
|
||||
if(!SetupUtil.tryDelete(new File(dst, files.next())))
|
||||
allDeleted = false;
|
||||
}
|
||||
|
||||
if(!recursiveDelete(new File(dst, "MCEFCache")))
|
||||
allDeleted = false;
|
||||
|
||||
//Destroy file listing and configs
|
||||
if(!fl.selfDestruct())
|
||||
allDeleted = false;
|
||||
|
||||
if(!SetupUtil.tryDelete(new File(configDir, "MCEF.cfg")))
|
||||
allDeleted = false;
|
||||
|
||||
if(!SetupUtil.tryDelete(new File(dst, "mcef.json")))
|
||||
allDeleted = false;
|
||||
|
||||
//Destroy mod file
|
||||
File curJar = SetupUtil.getSelfJarLocation();
|
||||
File mods = new File(dst, "mods");
|
||||
|
||||
if(mods.exists()) {
|
||||
File[] modList = mods.listFiles();
|
||||
|
||||
for(File f: modList) {
|
||||
String fname = f.getName().toLowerCase();
|
||||
|
||||
if(SetupUtil.areFileEqual(f, curJar)) {
|
||||
//Can't self-destruct JAR; add to delete-at-exit file list
|
||||
SetupUI.INSTANCE.initiateSelfDestruct(f);
|
||||
} else if(f.isFile() && fname.startsWith("mcef") && fname.endsWith(".jar")) {
|
||||
if(!SetupUtil.tryDelete(f))
|
||||
allDeleted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Show results
|
||||
if(allDeleted)
|
||||
JOptionPane.showMessageDialog(parent, "MCEF was successfully uninstalled!\nThanks for using it!", "All done!", JOptionPane.INFORMATION_MESSAGE);
|
||||
else
|
||||
JOptionPane.showMessageDialog(parent, "MCEF was uninstalled, but some files couldn't be removed; sorry about that...", "Almost everything done!", JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class SetupUI implements ActionListener, WindowListener, MouseListener {
|
||||
|
||||
public static SetupUI INSTANCE = null;
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
INSTANCE = new SetupUI();
|
||||
}
|
||||
|
||||
private File selfDestruct = null;
|
||||
private JFrame frame;
|
||||
private GridLayout layout;
|
||||
private JPanel mainPane;
|
||||
private JButton btnInstall;
|
||||
private JButton btnConfigure;
|
||||
private JButton btnUninstall;
|
||||
private JButton btnExit;
|
||||
private JLabel aboutLabel;
|
||||
|
||||
public SetupUI() {
|
||||
//Setup
|
||||
frame = new JFrame("MCEF Setup");
|
||||
frame.setMinimumSize(new Dimension(300, 100));
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
frame.addWindowListener(this);
|
||||
|
||||
//Layout & content
|
||||
btnInstall = new JButton("Install");
|
||||
btnConfigure = new JButton("Configure");
|
||||
btnUninstall = new JButton("Uninstall");
|
||||
btnExit = new JButton("Exit");
|
||||
|
||||
btnInstall.addActionListener(this);
|
||||
btnConfigure.addActionListener(this);
|
||||
btnUninstall.addActionListener(this);
|
||||
btnExit.addActionListener(this);
|
||||
|
||||
JPanel labelPane = new JPanel();
|
||||
labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.PAGE_AXIS));
|
||||
labelPane.add(new JLabel("Welcome to the MCEF Setup Wizard."));
|
||||
labelPane.add(new JLabel("What do you like to do?"));
|
||||
|
||||
mainPane = new JPanel();
|
||||
layout = new GridLayout(6, 1, 3, 3);
|
||||
mainPane.setBorder(new EmptyBorder(3, 3, 3, 3));
|
||||
mainPane.setLayout(layout);
|
||||
mainPane.add(labelPane);
|
||||
mainPane.add(btnInstall);
|
||||
mainPane.add(btnConfigure);
|
||||
mainPane.add(btnUninstall);
|
||||
mainPane.add(btnExit);
|
||||
|
||||
aboutLabel = new JLabel("<html><i>MCEF was written by <u><font color=\"#000099\">montoyo</font></u></i> </html>");
|
||||
aboutLabel.setHorizontalAlignment(JLabel.RIGHT);
|
||||
aboutLabel.addMouseListener(this);
|
||||
mainPane.add(aboutLabel);
|
||||
|
||||
//Display
|
||||
frame.setContentPane(mainPane);
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if(e.getSource() == btnExit)
|
||||
windowClosing(null);
|
||||
else if(e.getSource() == btnInstall)
|
||||
new McLocationPrompt(frame, "install");
|
||||
else if(e.getSource() == btnConfigure)
|
||||
new McLocationPrompt(frame, "configure");
|
||||
else if(e.getSource() == btnUninstall)
|
||||
new McLocationPrompt(frame, "uninstall");
|
||||
}
|
||||
|
||||
void initiateSelfDestruct(File f) {
|
||||
selfDestruct = f;
|
||||
}
|
||||
|
||||
void abortSelfDestruct() {
|
||||
selfDestruct = null;
|
||||
}
|
||||
|
||||
private void runSelfDestructionUnsafe() throws Throwable {
|
||||
File tmp = File.createTempFile("mcef-deleter", ".jar");
|
||||
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmp));
|
||||
InputStream is = SetupUI.class.getResourceAsStream("/net/montoyo/mcef/setup/Deleter.class");
|
||||
byte[] buf = new byte[8192];
|
||||
int read;
|
||||
|
||||
zos.putNextEntry(new ZipEntry("net/montoyo/mcef/setup/Deleter.class"));
|
||||
while((read = is.read(buf)) > 0)
|
||||
zos.write(buf, 0, read);
|
||||
|
||||
try {
|
||||
zos.closeEntry();
|
||||
} catch(Throwable t) {}
|
||||
|
||||
SetupUtil.silentClose(zos);
|
||||
SetupUtil.silentClose(is);
|
||||
|
||||
String java = "\"" + System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
|
||||
if(System.getProperty("os.name").toLowerCase().contains("win"))
|
||||
java += "w.exe";
|
||||
|
||||
java += "\" -classpath \"";
|
||||
java += tmp.getAbsolutePath();
|
||||
java += "\" net.montoyo.mcef.setup.Deleter \"";
|
||||
java += selfDestruct.getAbsolutePath();
|
||||
java += "\"";
|
||||
|
||||
System.out.println("Running auto-deleter:");
|
||||
System.out.println(java);
|
||||
Runtime.getRuntime().exec(java);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
frame.dispose();
|
||||
|
||||
if(selfDestruct != null) {
|
||||
try {
|
||||
runSelfDestructionUnsafe();
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Failed to destruct myself:");
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowIconified(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeiconified(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowActivated(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeactivated(WindowEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI("https://montoyo.net"));
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SetupUtil {
|
||||
|
||||
static boolean tryDelete(File f) {
|
||||
if(!f.exists())
|
||||
return true;
|
||||
|
||||
if(f.delete())
|
||||
return true;
|
||||
else {
|
||||
File dst = new File(f.getParentFile(), f.getName() + "_" + System.currentTimeMillis() + "_" + ((int) (Math.random() * 10000)) + ".tmp");
|
||||
|
||||
if(f.renameTo(dst)) {
|
||||
if(!dst.delete())
|
||||
dst.deleteOnExit();
|
||||
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static File getSelfJarLocation() {
|
||||
try {
|
||||
File ret = new File(SetupUtil.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
if(ret.exists() && ret.isFile())
|
||||
return ret;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could not locate own JAR (try #1):");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
File ret = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath());
|
||||
if(ret.exists() && ret.isFile())
|
||||
return ret;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could not locate own JAR (try #2):");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static void silentClose(Object o) {
|
||||
try {
|
||||
o.getClass().getMethod("close").invoke(o);
|
||||
} catch(Throwable t) {}
|
||||
}
|
||||
|
||||
static boolean copyFile(File src, File dst) {
|
||||
byte[] buf = new byte[65536];
|
||||
int read;
|
||||
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(src);
|
||||
FileOutputStream fos = new FileOutputStream(dst);
|
||||
|
||||
while((read = fis.read(buf)) > 0)
|
||||
fos.write(buf, 0, read);
|
||||
|
||||
silentClose(fos);
|
||||
silentClose(fis);
|
||||
return true;
|
||||
} catch(Throwable t) {
|
||||
System.err.println("Could NOT copy \"" + src.getAbsolutePath() + "\" to \"" + dst.getAbsolutePath() + "\":");
|
||||
t.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean areFileEqual(File a, File b) {
|
||||
if(a == null || b == null)
|
||||
return false;
|
||||
|
||||
try {
|
||||
String ap = a.getCanonicalPath();
|
||||
String bp = b.getCanonicalPath();
|
||||
|
||||
return System.getProperty("os.name").toLowerCase().contains("win") ? ap.equalsIgnoreCase(bp) : ap.equals(bp); //Windows paths are case-insensitive
|
||||
} catch(IOException e) {
|
||||
System.err.println("Could not compare file path, returning non-equal:");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package net.montoyo.mcef.setup;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
final class SlashComparator implements Comparator<String> {
|
||||
|
||||
private Comparator<String> fallback;
|
||||
|
||||
SlashComparator(Comparator<String> fb) {
|
||||
fallback = fb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(String a, String b) {
|
||||
int slashA = 0;
|
||||
int slashB = 0;
|
||||
|
||||
for(int i = 0; i < a.length(); i++) { //WARNING: Sub-optimized code here!
|
||||
if(a.charAt(i) == '/' || a.charAt(i) == '\\')
|
||||
slashA++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < b.length(); i++) {
|
||||
if(b.charAt(i) == '/' || b.charAt(i) == '\\')
|
||||
slashB++;
|
||||
}
|
||||
|
||||
int ret = slashB - slashA;
|
||||
if(ret == 0 && fallback != null)
|
||||
return fallback.compare(a, b);
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(obj == null)
|
||||
return false;
|
||||
else if(obj instanceof SlashComparator) {
|
||||
SlashComparator other = (SlashComparator) obj;
|
||||
|
||||
if(fallback == null)
|
||||
return other.fallback == null;
|
||||
else
|
||||
return other.fallback != null && fallback.equals(other.fallback);
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
/**
|
||||
* Dummy progress listener. Does nothing.
|
||||
* @author montoyo
|
||||
* @see Util#secure(IProgressListener)
|
||||
*
|
||||
*/
|
||||
public class DummyProgressListener implements IProgressListener {
|
||||
|
||||
@Override
|
||||
public void onProgressed(double d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskChanged(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressEnd() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
import net.minecraftforge.fml.common.ProgressManager;
|
||||
|
||||
public class ForgeProgressListener implements IProgressListener {
|
||||
|
||||
private ProgressManager.ProgressBar progressBar = null;
|
||||
private int lastVal = 0;
|
||||
|
||||
private void stepUntil(int val) {
|
||||
// Ensures progress is properly updated before popping
|
||||
while (lastVal < val && progressBar != null) {
|
||||
progressBar.step(lastVal + "%");
|
||||
lastVal++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressed(double d) {
|
||||
stepUntil((int) Util.clamp(d, 0.d, 100.d));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskChanged(String name) {
|
||||
if (progressBar != null) {
|
||||
stepUntil(100); // Ensure the last task is fully completed before popping
|
||||
ProgressManager.pop(progressBar);
|
||||
progressBar = null; // Reset progressBar after popping
|
||||
}
|
||||
|
||||
progressBar = ProgressManager.push(name, 100, false);
|
||||
lastVal = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressEnd() {
|
||||
if (progressBar != null) {
|
||||
stepUntil(100); // Ensure progress reaches 100% before popping
|
||||
ProgressManager.pop(progressBar);
|
||||
progressBar = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
|
||||
public class ForgeVersionParser {
|
||||
// Predefined mapping of Forge versions to Minecraft versions
|
||||
private static final Map<String, String> forgeVersionMapping = new HashMap<>();
|
||||
|
||||
static {
|
||||
// Mapping Forge versions to Minecraft versions
|
||||
forgeVersionMapping.put("forge-14.23.5.2859", "1.12.2");
|
||||
forgeVersionMapping.put("forge-14.23.5.2838", "1.12.2");
|
||||
forgeVersionMapping.put("forge-14.23.5.2768", "1.12.2");
|
||||
// Add more mappings here as needed
|
||||
}
|
||||
|
||||
// Method to parse the version
|
||||
public String parse(String version) {
|
||||
if (version == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the version contains "forge-"
|
||||
if (version.startsWith("forge-")) {
|
||||
// Look up the Forge version in the mapping table
|
||||
String realVersion = forgeVersionMapping.get(version);
|
||||
if (realVersion != null) {
|
||||
return realVersion;
|
||||
} else {
|
||||
// Handle unknown Forge version, maybe log a warning or use a default
|
||||
return "Unknown Minecraft Version (Forge)";
|
||||
}
|
||||
} else {
|
||||
// If it doesn't contain "forge-", it's likely a real Minecraft version
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
/**
|
||||
* This interface is used by classes that wants to keep track of the progress of some tasks.
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public interface IProgressListener {
|
||||
|
||||
/**
|
||||
* Call this when the current task progressed.
|
||||
* @param d The current task progress in percent.
|
||||
*/
|
||||
public void onProgressed(double d);
|
||||
|
||||
/**
|
||||
* Call this if the current task changed.
|
||||
* @param name The name of the new task.
|
||||
*/
|
||||
public void onTaskChanged(String name);
|
||||
|
||||
/**
|
||||
* Call this when everything is finished.
|
||||
*/
|
||||
public void onProgressEnd();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
/**
|
||||
* A set of functions to log messages into the MCEF log channel.
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public class Log {
|
||||
|
||||
public static void info(String what, Object... data) {
|
||||
LogManager.getLogger("MCEF").log(Level.INFO, String.format("[MCEF] " + what, data));
|
||||
}
|
||||
|
||||
public static void warning(String what, Object... data) {
|
||||
LogManager.getLogger("MCEF").log(Level.WARN, String.format("[MCEF] " + what, data));
|
||||
}
|
||||
|
||||
public static void error(String what, Object... data) {
|
||||
LogManager.getLogger("MCEF").log(Level.ERROR, String.format("[MCEF] " + what, data));
|
||||
}
|
||||
|
||||
public static void errorEx(String what, Throwable t, Object... data) {
|
||||
LogManager.getLogger("MCEF").log(Level.ERROR, String.format("[MCEF] " + what, data), t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import org.cef.OS;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class Platform {
|
||||
|
||||
public static String id;
|
||||
public static String idFull;
|
||||
public static String arch;
|
||||
public static String archFull;
|
||||
public static String PLATFORM;
|
||||
public static String PLATFORMFULL;
|
||||
|
||||
static { // Static block for initialization
|
||||
detectPlatform();
|
||||
}
|
||||
|
||||
private static void detectPlatform() {
|
||||
try {
|
||||
id = System.getProperty("os.name", "unknown").toLowerCase(Locale.US);
|
||||
arch = System.getProperty("os.arch", "unknown").toLowerCase(Locale.US);
|
||||
} catch (Exception e) {
|
||||
Log.error("Couldn't get OS name or architecture.");
|
||||
id = "unknown";
|
||||
arch = "unknown";
|
||||
}
|
||||
|
||||
// Normalize OS name
|
||||
if (OS.isWindows()) {
|
||||
id = "win";
|
||||
idFull = "windows";
|
||||
} else if (OS.isMacintosh()) {
|
||||
id = "mac";
|
||||
idFull = "macos";
|
||||
} else if (OS.isLinux()) {
|
||||
id = "linux";
|
||||
idFull = "linux";
|
||||
} else {
|
||||
Log.error("Your OS isn't supported by MCEF.");
|
||||
id = "unknown";
|
||||
idFull = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize CPU architecture
|
||||
if (arch.equals("x86_64") || arch.equals("amd64") || arch.equals("64")) {
|
||||
arch = "64";
|
||||
archFull = "amd64";
|
||||
} else if (arch.equals("aarch64") || arch.equals("arm64")) {
|
||||
if (!id.equals("mac") || !id.equals("darwin")) { // Only allow ARM64 for Macs
|
||||
Log.error("ARM-based platforms other than Mac are not supported by MCEF.");
|
||||
id = "unknown";
|
||||
idFull = "unknown";
|
||||
return;
|
||||
}
|
||||
arch = "arm64";
|
||||
archFull = "arm64";
|
||||
} else {
|
||||
Log.error("Your CPU architecture isn't supported by MCEF.");
|
||||
id = "unknown";
|
||||
idFull = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
// Set platform strings
|
||||
PLATFORM = id + arch;
|
||||
PLATFORMFULL = idFull + "_" + archFull;
|
||||
}
|
||||
|
||||
public static String getOS() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static String getPlatform() {
|
||||
return PLATFORM;
|
||||
}
|
||||
|
||||
public static String getPlatformFull() {
|
||||
return PLATFORMFULL;
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
return "win".equals(id);
|
||||
}
|
||||
|
||||
public static boolean isMacOS() {
|
||||
return "mac".equals(id) || "darwin".equals(id);
|
||||
}
|
||||
|
||||
public static boolean isLinux() {
|
||||
return "linux".equals(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.remote.Mirror;
|
||||
import net.montoyo.mcef.remote.MirrorManager;
|
||||
import org.cef.OS;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class Util {
|
||||
|
||||
private static final DummyProgressListener DPH = new DummyProgressListener();
|
||||
|
||||
/**
|
||||
* Clamps d between min and max.
|
||||
*
|
||||
* @param d The value to clamp.
|
||||
* @param min The minimum.
|
||||
* @param max The maximum.
|
||||
* @return The clamped value.
|
||||
*/
|
||||
public static double clamp(double d, double min, double max) {
|
||||
if(d < min)
|
||||
return min;
|
||||
else if(d > max)
|
||||
return max;
|
||||
else
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a ZIP archive into a folder.
|
||||
*
|
||||
* @param zip The ZIP archive file to extract.
|
||||
* @param out The output directory for the ZIP content.
|
||||
* @return true if the extraction was successful.
|
||||
*/
|
||||
public static boolean extractZip(File zip, File out) {
|
||||
// For macOS, the "unzip" utility seems to be reliable at setting certain flags on executables when extracting
|
||||
// Otherwise, extracting a .app is a pain. It refuses to run without setting executable flags on the contents, etc
|
||||
if (OS.isMacintosh()) {
|
||||
try {
|
||||
Process unzip = Runtime.getRuntime().exec(new String[]{"/usr/bin/unzip", zip.getAbsolutePath(), "-d", out.getAbsolutePath()});
|
||||
unzip.waitFor();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ZipInputStream zis;
|
||||
|
||||
try {
|
||||
zis = new ZipInputStream(new FileInputStream(zip));
|
||||
} catch(FileNotFoundException e) {
|
||||
Log.error("Couldn't extract %s: File not found.", zip.getName());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ZipEntry ze;
|
||||
while((ze = zis.getNextEntry()) != null) {
|
||||
if(ze.isDirectory())
|
||||
continue;
|
||||
|
||||
File dst = new File(out, ze.getName());
|
||||
delete(dst);
|
||||
mkdirs(dst);
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(dst);
|
||||
byte[] data = new byte[65536];
|
||||
int read;
|
||||
|
||||
while((read = zis.read(data)) > 0)
|
||||
fos.write(data, 0, read);
|
||||
|
||||
close(fos);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch(FileNotFoundException e) {
|
||||
Log.error("Couldn't extract a file from %s. Maybe you're missing some permissions?", zip.getName());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch(IOException e) {
|
||||
Log.error("IOException while extracting %s.", zip.getName());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
close(zis);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from a URL.
|
||||
*
|
||||
* @param urlString The URL to download from.
|
||||
* @param outputFile The file to save the downloaded content to.
|
||||
* @param ph A progress handler.
|
||||
* @throws IOException If the download fails.
|
||||
*/
|
||||
public static void downloadFile(String urlString, File outputFile, IProgressListener ph) throws IOException {
|
||||
try {
|
||||
Log.info("Downloading: " + urlString + " -> " + outputFile.getCanonicalPath());
|
||||
|
||||
ph = secure(ph);
|
||||
ph.onTaskChanged("Downloading " + outputFile.getName());
|
||||
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection urlConnection;
|
||||
int statusCode;
|
||||
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// Set custom User-Agent
|
||||
String userAgent = "MCEF/3.0 (Java/" + System.getProperty("java.version") + "; " + System.getProperty("os.name") + " " + System.getProperty("os.arch") + ")";
|
||||
urlConnection.setRequestProperty("User-Agent", userAgent);
|
||||
|
||||
urlConnection.connect();
|
||||
|
||||
statusCode = urlConnection.getResponseCode();
|
||||
|
||||
if (statusCode != HttpURLConnection.HTTP_OK) {
|
||||
Log.info("Error: " + urlString + " returned HTTP " + statusCode);
|
||||
throw new IOException("Server returned HTTP response code: " + statusCode);
|
||||
}
|
||||
|
||||
int fileSize = urlConnection.getContentLength();
|
||||
BufferedInputStream inputStream = new BufferedInputStream(urlConnection.getInputStream());
|
||||
FileOutputStream outputStream = new FileOutputStream(outputFile);
|
||||
|
||||
byte[] buffer = new byte[2048];
|
||||
int count;
|
||||
int readBytes = 0;
|
||||
while ((count = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, count);
|
||||
readBytes += count;
|
||||
float percentComplete = (float) readBytes / fileSize;
|
||||
ph.onProgressed(percentComplete * 100);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.info(e.getMessage());
|
||||
throw new IOException("Failed to download " + urlString, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #downloadFile(String, File, IProgressListener)} but returns true if the download was successful.
|
||||
*
|
||||
* @param urlString The URL to download from.
|
||||
* @param outputFile The file to save the downloaded content to.
|
||||
* @param ph A progress handler.
|
||||
* @throws IOException If the download fails.
|
||||
*/
|
||||
public static boolean downloadFileSafe(String urlString, File outputFile, IProgressListener ph) {
|
||||
try {
|
||||
// Check if the file path (dir) exists, if not create it
|
||||
mkdirs(outputFile);
|
||||
// Download the file
|
||||
downloadFile(urlString, outputFile, ph);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.error("Failed to download %s", urlString);
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a tar.gz file.
|
||||
*
|
||||
* @param tarGzFile The tar.gz file to extract.
|
||||
* @param outputDirectory The directory to extract files to.
|
||||
*/
|
||||
public static void extractTarGz(File tarGzFile, File outputDirectory, IProgressListener ph) {
|
||||
ph = secure(ph);
|
||||
ph.onTaskChanged("Extracting " + tarGzFile.getName());
|
||||
|
||||
outputDirectory.mkdirs();
|
||||
long fileSize = tarGzFile.length();
|
||||
long totalBytesRead = 0;
|
||||
|
||||
try (TarArchiveInputStream tarInput = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(tarGzFile)))) {
|
||||
TarArchiveEntry entry;
|
||||
while ((entry = tarInput.getNextTarEntry()) != null) {
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File outputFile = new File(outputDirectory, entry.getName());
|
||||
outputFile.getParentFile().mkdirs();
|
||||
|
||||
try (OutputStream outputStream = new FileOutputStream(outputFile)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = tarInput.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
float percentComplete = ((float) totalBytesRead / fileSize) / 2.6158204f; // Rough compression ratio
|
||||
ph.onProgressed(percentComplete * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.error("Failed to extract gzip file to " + outputDirectory, e);
|
||||
}
|
||||
|
||||
ph.onProgressed(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function. Secures a progress listener.
|
||||
* If pl is null, then a dummy empty progress listener will be returned.
|
||||
*
|
||||
* @param pl The progress handler to secure.
|
||||
* @return A progress handler that is never null.
|
||||
* @see IProgressListener
|
||||
*/
|
||||
public static IProgressListener secure(IProgressListener pl) {
|
||||
return (pl == null) ? DPH : pl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file using a string.
|
||||
*
|
||||
* @param src The file to rename.
|
||||
* @param name The new name of the file.
|
||||
* @return the new file or null if it failed.
|
||||
*/
|
||||
public static File rename(File src, String name) {
|
||||
File ret = new File(src.getParentFile(), name);
|
||||
|
||||
if(src.renameTo(ret))
|
||||
return ret;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the directory in which the file is exists.
|
||||
* If this one doesn't exist, i'll be created.
|
||||
*
|
||||
* @param f The file.
|
||||
*/
|
||||
public static void mkdirs(File f) {
|
||||
File p = f.getParentFile();
|
||||
if(!p.exists())
|
||||
p.mkdirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to delete a file in an advanced way.
|
||||
* Does a warning in log if it couldn't delete it neither rename it.
|
||||
*
|
||||
* @param f The file to be deleted.
|
||||
* @see #delete(File)
|
||||
*/
|
||||
public static void delete(String f) {
|
||||
delete(new File(f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to delete a file in an advanced way.
|
||||
* Does a warning in log if it couldn't delete it neither rename it.
|
||||
*
|
||||
* @param f The file to be deleted.
|
||||
* @see #delete(String)
|
||||
*/
|
||||
public static void delete(File f) {
|
||||
if(!f.exists() || f.delete())
|
||||
return;
|
||||
|
||||
File mv = new File(f.getParentFile(), "deleteme" + ((int) (Math.random() * 100000.d)));
|
||||
if(f.renameTo(mv)) {
|
||||
if(!mv.delete())
|
||||
mv.deleteOnExit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Log.warning("Couldn't delete file! If there's any problems, please try to remove it yourself. Path: %s", f.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls "close" on the specified object without throwing any exceptions.
|
||||
* This is usefull with input and output streams.
|
||||
*
|
||||
* @param o The object to call close on.
|
||||
*/
|
||||
public static void close(Object o) {
|
||||
try {
|
||||
o.getClass().getMethod("close").invoke(o);
|
||||
} catch(Throwable t) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link Files#isSameFile(Path, Path)} but if an {@link IOException} is thrown,
|
||||
* return false.
|
||||
*
|
||||
* @param p1 Path 1
|
||||
* @param p2 Path 2
|
||||
* @return true if the paths are the same, false if they are not or if an exception is thrown during the comparison
|
||||
*/
|
||||
public static boolean isSameFile(Path p1, Path p2) {
|
||||
try {
|
||||
return Files.isSameFile(p1, p2);
|
||||
} catch(IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link System#getenv(String)}, but if no such environment variable is
|
||||
* defined, will return an empty string instead of null.
|
||||
*
|
||||
* @param name Name of the environment variable to get
|
||||
* @return The value of this environment variable (may be empty but never null)
|
||||
*/
|
||||
public static String getenv(String name) {
|
||||
String ret = System.getenv(name);
|
||||
return ret == null ? "" : ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package net.montoyo.mcef.utilities;
|
||||
|
||||
/**
|
||||
* A utility class used for parsing decimal dot-separated versions from strings and comparing them.
|
||||
* Example: 5.4.9
|
||||
*
|
||||
* @author montoyo
|
||||
*
|
||||
*/
|
||||
public class Version {
|
||||
|
||||
private int[] numbers;
|
||||
|
||||
/**
|
||||
* Constructs a version from a decimal dot-separated version string.
|
||||
* @param v A version string, such as 5.4.9
|
||||
*/
|
||||
public Version(String v) {
|
||||
String[] ray = v.trim().split("\\.");
|
||||
numbers = new int[ray.length];
|
||||
|
||||
for(int i = 0; i < ray.length; i++) {
|
||||
try {
|
||||
numbers[i] = Integer.parseInt(ray[i]);
|
||||
} catch(NumberFormatException e) {
|
||||
Log.error("Couldn't parse %s. Number %d will be zero.", v, i);
|
||||
e.printStackTrace();
|
||||
numbers[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Look for useless ending zeroes
|
||||
int end;
|
||||
for(end = numbers.length - 1; end >= 0; end--) {
|
||||
if(numbers[end] != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
end++;
|
||||
if(end != numbers.length) { //Is there any? Remove them!
|
||||
int[] na = new int[end];
|
||||
System.arraycopy(numbers, 0, na, 0, end);
|
||||
numbers = na;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two versions. Return true if this instance is bigger than v.
|
||||
* If both describes the same version, false is returned.
|
||||
*
|
||||
* @param v The version to compare with.
|
||||
* @return true if this instance is bigger than v.
|
||||
*/
|
||||
public boolean isBiggerThan(Version v) {
|
||||
int len = Math.min(numbers.length, v.numbers.length);
|
||||
|
||||
for(int i = 0; i < len; i++) {
|
||||
if(numbers[i] > v.numbers[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return numbers.length > v.numbers.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two versions. Returns true if this instance is equal to o.
|
||||
* @param o The version to compare with.
|
||||
* @return true if both objects describes the same version.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(!(o instanceof Version))
|
||||
return false;
|
||||
|
||||
Version v = (Version) o;
|
||||
if(v == this)
|
||||
return true;
|
||||
|
||||
if(numbers.length != v.numbers.length)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < numbers.length; i++) {
|
||||
if(numbers[i] != v.numbers[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns this version class into a decimal dot-separated version string.
|
||||
* @return a version string, such as 5.4.9
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String ret = "";
|
||||
for(int i = 0; i < numbers.length; i++) {
|
||||
if(i > 0)
|
||||
ret += '.';
|
||||
|
||||
ret += numbers[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package net.montoyo.mcef.virtual;
|
||||
|
||||
import net.montoyo.mcef.api.IBrowser;
|
||||
import net.montoyo.mcef.api.IStringVisitor;
|
||||
|
||||
public class VirtualBrowser implements IBrowser {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(double x1, double y1, double x2, double y2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextureID() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMouseMove(int x, int y, int mods, boolean left) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMouseButton(int x, int y, int mods, int btn, boolean pressed, int ccnt) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectKeyTyped(char c, int mods) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectKeyPressedByKeyCode(int keyCode, char c, int mods) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectKeyReleasedByKeyCode(int keyCode, char c, int mods) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMouseWheel(int x, int y, int mods, int amount, int rot) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runJS(String script, String frame) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadURL(String url) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goBack() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goForward() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURL() {
|
||||
return "about:blank";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(IStringVisitor isv) {
|
||||
isv.visit("https://www.youtube.com/watch?v=VX5gXHcbJAk");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPageLoading() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef;
|
||||
|
||||
import org.cef.callback.CefSchemeHandlerFactory;
|
||||
import org.cef.handler.CefAppHandler;
|
||||
import org.cef.handler.CefAppHandlerAdapter;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Exposes static methods for managing the global CEF context.
|
||||
*/
|
||||
public class CefApp extends CefAppHandlerAdapter {
|
||||
public final class CefVersion {
|
||||
public final int JCEF_COMMIT_NUMBER;
|
||||
|
||||
public final int CEF_VERSION_MAJOR;
|
||||
public final int CEF_VERSION_MINOR;
|
||||
public final int CEF_VERSION_PATCH;
|
||||
public final int CEF_COMMIT_NUMBER;
|
||||
|
||||
public final int CHROME_VERSION_MAJOR;
|
||||
public final int CHROME_VERSION_MINOR;
|
||||
public final int CHROME_VERSION_BUILD;
|
||||
public final int CHROME_VERSION_PATCH;
|
||||
|
||||
private CefVersion(int jcefCommitNo, int cefMajor, int cefMinor, int cefPatch,
|
||||
int cefCommitNo, int chrMajor, int chrMin, int chrBuild, int chrPatch) {
|
||||
JCEF_COMMIT_NUMBER = jcefCommitNo;
|
||||
|
||||
CEF_VERSION_MAJOR = cefMajor;
|
||||
CEF_VERSION_MINOR = cefMinor;
|
||||
CEF_VERSION_PATCH = cefPatch;
|
||||
CEF_COMMIT_NUMBER = cefCommitNo;
|
||||
|
||||
CHROME_VERSION_MAJOR = chrMajor;
|
||||
CHROME_VERSION_MINOR = chrMin;
|
||||
CHROME_VERSION_BUILD = chrBuild;
|
||||
CHROME_VERSION_PATCH = chrPatch;
|
||||
}
|
||||
|
||||
public String getJcefVersion() {
|
||||
return CEF_VERSION_MAJOR + "." + CEF_VERSION_MINOR + "." + CEF_VERSION_PATCH + "."
|
||||
+ JCEF_COMMIT_NUMBER;
|
||||
}
|
||||
|
||||
public String getCefVersion() {
|
||||
return CEF_VERSION_MAJOR + "." + CEF_VERSION_MINOR + "." + CEF_VERSION_PATCH;
|
||||
}
|
||||
|
||||
public String getChromeVersion() {
|
||||
return CHROME_VERSION_MAJOR + "." + CHROME_VERSION_MINOR + "." + CHROME_VERSION_BUILD
|
||||
+ "." + CHROME_VERSION_PATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JCEF Version = " + getJcefVersion() + "\n"
|
||||
+ "CEF Version = " + getCefVersion() + "\n"
|
||||
+ "Chromium Version = " + getChromeVersion();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The CefAppState gives you a hint if the CefApp is already usable or not
|
||||
* usable any more. See values for details.
|
||||
*/
|
||||
public enum CefAppState {
|
||||
/**
|
||||
* No CefApp instance was created yet. Call getInstance() to create a new
|
||||
* one.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* CefApp is new created but not initialized yet. No CefClient and no
|
||||
* CefBrowser was created until now.
|
||||
*/
|
||||
NEW,
|
||||
|
||||
/**
|
||||
* CefApp is in its initializing process. Please wait until initializing is
|
||||
* finished.
|
||||
*/
|
||||
INITIALIZING,
|
||||
|
||||
/**
|
||||
* CefApp is up and running. At least one CefClient was created and the
|
||||
* message loop is running. You can use all classes and methods of JCEF now.
|
||||
*/
|
||||
INITIALIZED,
|
||||
|
||||
/**
|
||||
* CefApp is in its shutdown process. All CefClients and CefBrowser
|
||||
* instances will be disposed. No new CefClient or CefBrowser is allowed to
|
||||
* be created. The message loop will be performed until all CefClients and
|
||||
* all CefBrowsers are disposed completely.
|
||||
*/
|
||||
SHUTTING_DOWN,
|
||||
|
||||
/**
|
||||
* CefApp is terminated and can't be used any more. You can shutdown the
|
||||
* application safely now.
|
||||
*/
|
||||
TERMINATED
|
||||
}
|
||||
|
||||
/**
|
||||
* According the singleton pattern, this attribute keeps
|
||||
* one single object of this class.
|
||||
*/
|
||||
private static CefApp self = null;
|
||||
private static CefAppHandler appHandler_ = null;
|
||||
private static CefAppState state_ = CefAppState.NONE;
|
||||
private Timer workTimer_ = null;
|
||||
private HashSet<CefClient> clients_ = new HashSet<CefClient>();
|
||||
private CefSettings settings_ = null;
|
||||
|
||||
/**
|
||||
* To get an instance of this class, use the method
|
||||
* getInstance() instead of this CTOR.
|
||||
* <p>
|
||||
* The CTOR is called by getInstance() as needed and
|
||||
* loads all required JCEF libraries.
|
||||
*
|
||||
* @throws UnsatisfiedLinkError
|
||||
*/
|
||||
private CefApp(String[] args, CefSettings settings) throws UnsatisfiedLinkError {
|
||||
super(args);
|
||||
if (settings != null) settings_ = settings.clone();
|
||||
|
||||
if (appHandler_ == null) {
|
||||
appHandler_ = this;
|
||||
}
|
||||
|
||||
if (!N_PreInitialize())
|
||||
throw new IllegalStateException("Failed to pre-initialize native code");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign an AppHandler to CefApp. The AppHandler can be used to evaluate
|
||||
* application arguments, to register your own schemes and to hook into the
|
||||
* shutdown sequence. See CefAppHandler for more details.
|
||||
* <p>
|
||||
* This method must be called before CefApp is initialized. CefApp will be
|
||||
* initialized automatically if you call createClient() the first time.
|
||||
*
|
||||
* @param appHandler An instance of CefAppHandler.
|
||||
* @throws IllegalStateException in case of CefApp is already initialized
|
||||
*/
|
||||
public static void addAppHandler(CefAppHandler appHandler) throws IllegalStateException {
|
||||
if (getState().compareTo(CefAppState.NEW) > 0)
|
||||
throw new IllegalStateException("Must be called before CefApp is initialized");
|
||||
appHandler_ = appHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of this class.
|
||||
*
|
||||
* @return an instance of this class
|
||||
* @throws UnsatisfiedLinkError
|
||||
*/
|
||||
public static synchronized CefApp getInstance() throws UnsatisfiedLinkError {
|
||||
return getInstance(null, null);
|
||||
}
|
||||
|
||||
public static synchronized CefApp getInstance(String[] args) throws UnsatisfiedLinkError {
|
||||
return getInstance(args, null);
|
||||
}
|
||||
|
||||
public static synchronized CefApp getInstance(CefSettings settings)
|
||||
throws UnsatisfiedLinkError {
|
||||
return getInstance(null, settings);
|
||||
}
|
||||
|
||||
public static synchronized CefApp getInstance(String[] args, CefSettings settings)
|
||||
throws UnsatisfiedLinkError {
|
||||
if (settings != null) {
|
||||
if (getState() != CefAppState.NONE && getState() != CefAppState.NEW)
|
||||
throw new IllegalStateException("Settings can only be passed to CEF"
|
||||
+ " before createClient is called the first time.");
|
||||
}
|
||||
if (self == null) {
|
||||
if (getState() == CefAppState.TERMINATED)
|
||||
throw new IllegalStateException("CefApp was terminated");
|
||||
self = new CefApp(args, settings);
|
||||
setState(CefAppState.NEW);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
public final void setSettings(CefSettings settings) throws IllegalStateException {
|
||||
if (getState() != CefAppState.NONE && getState() != CefAppState.NEW)
|
||||
throw new IllegalStateException("Settings can only be passed to CEF"
|
||||
+ " before createClient is called the first time.");
|
||||
settings_ = settings.clone();
|
||||
}
|
||||
|
||||
public final CefVersion getVersion() {
|
||||
try {
|
||||
return N_GetVersion();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of CefApp.
|
||||
*
|
||||
* @return current state.
|
||||
*/
|
||||
public final static CefAppState getState() {
|
||||
synchronized (state_) {
|
||||
return state_;
|
||||
}
|
||||
}
|
||||
|
||||
private static final void setState(final CefAppState state) {
|
||||
synchronized (state_) {
|
||||
state_ = state;
|
||||
}
|
||||
if (appHandler_ != null) appHandler_.stateHasChanged(state);
|
||||
}
|
||||
|
||||
//montoyo: Added for MCEF
|
||||
public static final void forceShutdownState() {
|
||||
synchronized (state_) {
|
||||
state_ = CefAppState.SHUTTING_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To shutdown the system, it's important to call the dispose
|
||||
* method. Calling this method closes all client instances with
|
||||
* and all browser instances each client owns. After that the
|
||||
* message loop is terminated and CEF is shutdown.
|
||||
*/
|
||||
public synchronized final void dispose() {
|
||||
switch (getState()) {
|
||||
case NEW:
|
||||
// Nothing to do inspite of invalidating the state
|
||||
setState(CefAppState.TERMINATED);
|
||||
break;
|
||||
|
||||
case INITIALIZING:
|
||||
case INITIALIZED:
|
||||
// (3) Shutdown sequence. Close all clients and continue.
|
||||
setState(CefAppState.SHUTTING_DOWN);
|
||||
if (clients_.isEmpty()) {
|
||||
shutdown();
|
||||
} else {
|
||||
// shutdown() will be called from clientWasDisposed() when the last
|
||||
// client is gone.
|
||||
// Use a copy of the HashSet to avoid iterating during modification.
|
||||
HashSet<CefClient> clients = new HashSet<CefClient>(clients_);
|
||||
for (CefClient c : clients) {
|
||||
c.dispose();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NONE:
|
||||
case SHUTTING_DOWN:
|
||||
case TERMINATED:
|
||||
// Ignore shutdown, CefApp is already terminated, in shutdown progress
|
||||
// or was never created (shouldn't be possible)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client instance and returns it to the caller.
|
||||
* One client instance is responsible for one to many browser
|
||||
* instances
|
||||
*
|
||||
* @return a new client instance
|
||||
*/
|
||||
public synchronized CefClient createClient() {
|
||||
switch (getState()) {
|
||||
case NEW:
|
||||
setState(CefAppState.INITIALIZING);
|
||||
initialize();
|
||||
// FALL THRU
|
||||
|
||||
case INITIALIZING:
|
||||
case INITIALIZED:
|
||||
CefClient client = new CefClient();
|
||||
clients_.add(client);
|
||||
return client;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("Can't crate client in state " + state_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a scheme handler factory for the specified |scheme_name| and
|
||||
* optional |domain_name|. An empty |domain_name| value for a standard scheme
|
||||
* will cause the factory to match all domain names. The |domain_name| value
|
||||
* will be ignored for non-standard schemes. If |scheme_name| is a built-in
|
||||
* scheme and no handler is returned by |factory| then the built-in scheme
|
||||
* handler factory will be called. If |scheme_name| is a custom scheme then
|
||||
* also implement the CefApp::OnRegisterCustomSchemes() method in all
|
||||
* processes. This function may be called multiple times to change or remove
|
||||
* the factory that matches the specified |scheme_name| and optional
|
||||
* |domain_name|. Returns false if an error occurs. This function may be
|
||||
* called on any thread in the browser process.
|
||||
*/
|
||||
public boolean registerSchemeHandlerFactory(
|
||||
String schemeName, String domainName, CefSchemeHandlerFactory factory) {
|
||||
try {
|
||||
return N_RegisterSchemeHandlerFactory(schemeName, domainName, factory);
|
||||
} catch (Exception err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all registered scheme handler factories. Returns false on error. This
|
||||
* function may be called on any thread in the browser process.
|
||||
*/
|
||||
public boolean clearSchemeHandlerFactories() {
|
||||
try {
|
||||
return N_ClearSchemeHandlerFactories();
|
||||
} catch (Exception err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by a CefClient if it was disposed. This causes
|
||||
* CefApp to clean up its list of available client instances. If all clients
|
||||
* are disposed, CefApp will be shutdown.
|
||||
*
|
||||
* @param client the disposed client.
|
||||
*/
|
||||
protected final synchronized void clientWasDisposed(CefClient client) {
|
||||
clients_.remove(client);
|
||||
if (clients_.isEmpty() && getState().compareTo(CefAppState.SHUTTING_DOWN) >= 0) {
|
||||
// Shutdown native system.
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the context.
|
||||
*
|
||||
* @return true on success.
|
||||
*/
|
||||
private final void initialize() {
|
||||
String jcefPath = getJcefLibPath();
|
||||
System.out.println("initialize on " + Thread.currentThread() + " with library path " + jcefPath);
|
||||
|
||||
CefSettings settings = settings_ != null ? settings_ : new CefSettings();
|
||||
|
||||
if (OS.isWindows()) {
|
||||
Path jcefHelperPath = Paths.get(jcefPath, "jcef_helper.exe");
|
||||
settings.browser_subprocess_path = jcefHelperPath.normalize().toAbsolutePath().toString();
|
||||
} else if (OS.isMacintosh()) {
|
||||
String basePath = Paths.get(jcefPath).getParent().getParent().toString();
|
||||
settings.main_bundle_path = basePath;
|
||||
settings.framework_dir_path = basePath
|
||||
+ "/Contents/Frameworks/Chromium Embedded Framework.framework";
|
||||
settings.locales_dir_path = basePath
|
||||
+ "/Contents/Frameworks/Chromium Embedded Framework.framework/Resources";
|
||||
settings.resources_dir_path = basePath
|
||||
+ "/Contents/Frameworks/Chromium Embedded Framework.framework/Resources";
|
||||
settings.browser_subprocess_path = basePath
|
||||
+ "/Contents/Frameworks/jcef Helper.app/Contents/MacOS/jcef Helper";
|
||||
} else if (OS.isLinux()) {
|
||||
settings.resources_dir_path = jcefPath;
|
||||
Path jcefHelperPath = Paths.get(jcefPath, "jcef_helper");
|
||||
settings.browser_subprocess_path = jcefHelperPath.normalize().toAbsolutePath().toString();
|
||||
Path localesPath = Paths.get(jcefPath, "locales");
|
||||
settings.locales_dir_path = localesPath.normalize().toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
if (N_Initialize(appHandler_, settings)) setState(CefAppState.INITIALIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked by the native code (currently on Mac only) in case
|
||||
* of a termination event (e.g. someone pressed CMD+Q).
|
||||
*/
|
||||
protected final void handleBeforeTerminate() {
|
||||
System.out.println("Cmd+Q termination request.");
|
||||
CefAppHandler handler =
|
||||
(CefAppHandler) ((appHandler_ == null) ? this : appHandler_);
|
||||
if (!handler.onBeforeTerminate()) dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shut down the context.
|
||||
*/
|
||||
private final void shutdown() {
|
||||
System.out.println("shutdown on " + Thread.currentThread());
|
||||
|
||||
// Shutdown native CEF.
|
||||
N_Shutdown();
|
||||
|
||||
setState(CefAppState.TERMINATED);
|
||||
CefApp.self = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a single message loop iteration. Used on all platforms except
|
||||
* Windows with windowed rendering.
|
||||
*/
|
||||
public final void doMessageLoopWork(final long delay_ms) {
|
||||
// Execute on the AWT event dispatching thread.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (getState() == CefAppState.TERMINATED) return;
|
||||
|
||||
// The maximum number of milliseconds we're willing to wait between
|
||||
// calls to DoMessageLoopWork().
|
||||
final long kMaxTimerDelay = 1000 / 30; // 30fps
|
||||
|
||||
if (workTimer_ != null) {
|
||||
workTimer_.stop();
|
||||
workTimer_ = null;
|
||||
}
|
||||
|
||||
if (delay_ms <= 0) {
|
||||
// Execute the work immediately.
|
||||
N_DoMessageLoopWork();
|
||||
|
||||
// Schedule more work later.
|
||||
doMessageLoopWork(kMaxTimerDelay);
|
||||
} else {
|
||||
long timer_delay_ms = delay_ms;
|
||||
// Never wait longer than the maximum allowed time.
|
||||
if (timer_delay_ms > kMaxTimerDelay) timer_delay_ms = kMaxTimerDelay;
|
||||
|
||||
workTimer_ = new Timer((int) timer_delay_ms, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
// Timer has timed out.
|
||||
workTimer_.stop();
|
||||
workTimer_ = null;
|
||||
|
||||
N_DoMessageLoopWork();
|
||||
|
||||
// Schedule more work later.
|
||||
doMessageLoopWork(kMaxTimerDelay);
|
||||
}
|
||||
});
|
||||
workTimer_.start();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be called at the beginning of the main() method to perform platform-
|
||||
* specific startup initialization. On Linux this initializes Xlib multithreading and on
|
||||
* macOS this dynamically loads the CEF framework.
|
||||
*
|
||||
* @param args Command-line arguments massed to main().
|
||||
* @return True on successful startup.
|
||||
*/
|
||||
public static final boolean startup(String[] args) {
|
||||
String jcefPath = getJcefLibPath();
|
||||
|
||||
if (OS.isWindows()) {
|
||||
System.load(jcefPath + "\\d3dcompiler_47.dll");
|
||||
System.load(jcefPath + "\\libGLESv2.dll");
|
||||
System.load(jcefPath + "\\libEGL.dll");
|
||||
System.load(jcefPath + "\\chrome_elf.dll");
|
||||
System.load(jcefPath + "\\libcef.dll");
|
||||
System.load(jcefPath + "\\jcef.dll");
|
||||
return true;
|
||||
} else if (OS.isMacintosh()) {
|
||||
System.load(jcefPath + "/libjcef.dylib");
|
||||
return N_Startup(getCefFrameworkPath(args));
|
||||
} else if (OS.isLinux()) {
|
||||
System.load(jcefPath + "/libcef.so");
|
||||
System.load(jcefPath + "/libjcef.so");
|
||||
return N_Startup(null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final String getJcefLibPath() {
|
||||
Path runtimeDir = Paths.get("");
|
||||
final Path jcefPath;
|
||||
if (OS.isWindows() || OS.isLinux()) {
|
||||
jcefPath = runtimeDir.resolve("jcef/cef_runtime");
|
||||
} else if (OS.isMacintosh()) {
|
||||
jcefPath = runtimeDir.resolve("jcef/jcef_app.app/Contents/Java");
|
||||
} else {
|
||||
jcefPath = null;
|
||||
}
|
||||
return jcefPath != null ? jcefPath.toAbsolutePath().toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path that contains the CEF Framework on macOS.
|
||||
*
|
||||
* @return The path to the CEF Framework.
|
||||
*/
|
||||
private static final String getCefFrameworkPath(String[] args) {
|
||||
// Check for the path on the command-line.
|
||||
String switchPrefix = "--framework-dir-path=";
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith(switchPrefix)) {
|
||||
return new File(arg.substring(switchPrefix.length())).getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the path relative to the JCEF lib location in the app bundle.
|
||||
return new File(getJcefLibPath() + "/../Frameworks/Chromium Embedded Framework.framework")
|
||||
.getAbsolutePath();
|
||||
}
|
||||
|
||||
private final static native boolean N_Startup(String pathToCefFramework);
|
||||
|
||||
private final native boolean N_PreInitialize();
|
||||
|
||||
private final native boolean N_Initialize(CefAppHandler appHandler, CefSettings settings);
|
||||
|
||||
public final native void N_Shutdown();
|
||||
|
||||
public final native void N_DoMessageLoopWork();
|
||||
|
||||
private final native CefVersion N_GetVersion();
|
||||
|
||||
private final native boolean N_RegisterSchemeHandlerFactory(
|
||||
String schemeName, String domainName, CefSchemeHandlerFactory factory);
|
||||
|
||||
private final native boolean N_ClearSchemeHandlerFactories();
|
||||
}
|
||||
@@ -0,0 +1,869 @@
|
||||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef;
|
||||
|
||||
import org.cef.browser.CefBrowser;
|
||||
import org.cef.browser.CefBrowserFactory;
|
||||
import org.cef.browser.CefFrame;
|
||||
import org.cef.browser.CefMessageRouter;
|
||||
import org.cef.browser.CefRequestContext;
|
||||
import org.cef.callback.CefAuthCallback;
|
||||
import org.cef.callback.CefBeforeDownloadCallback;
|
||||
import org.cef.callback.CefCallback;
|
||||
import org.cef.callback.CefContextMenuParams;
|
||||
import org.cef.callback.CefDownloadItem;
|
||||
import org.cef.callback.CefDownloadItemCallback;
|
||||
import org.cef.callback.CefDragData;
|
||||
import org.cef.callback.CefFileDialogCallback;
|
||||
import org.cef.callback.CefJSDialogCallback;
|
||||
import org.cef.callback.CefMenuModel;
|
||||
import org.cef.callback.CefPrintDialogCallback;
|
||||
import org.cef.callback.CefPrintJobCallback;
|
||||
import org.cef.handler.CefClientHandler;
|
||||
import org.cef.handler.CefContextMenuHandler;
|
||||
import org.cef.handler.CefDialogHandler;
|
||||
import org.cef.handler.CefDisplayHandler;
|
||||
import org.cef.handler.CefDownloadHandler;
|
||||
import org.cef.handler.CefDragHandler;
|
||||
import org.cef.handler.CefFocusHandler;
|
||||
import org.cef.handler.CefJSDialogHandler;
|
||||
import org.cef.handler.CefKeyboardHandler;
|
||||
import org.cef.handler.CefLifeSpanHandler;
|
||||
import org.cef.handler.CefLoadHandler;
|
||||
import org.cef.handler.CefPrintHandler;
|
||||
import org.cef.handler.CefRenderHandler;
|
||||
import org.cef.handler.CefRequestHandler;
|
||||
import org.cef.handler.CefResourceHandler;
|
||||
import org.cef.handler.CefResourceRequestHandler;
|
||||
import org.cef.handler.CefScreenInfo;
|
||||
import org.cef.handler.CefWindowHandler;
|
||||
import org.cef.misc.BoolRef;
|
||||
import org.cef.misc.CefPrintSettings;
|
||||
import org.cef.misc.StringRef;
|
||||
import org.cef.network.CefRequest;
|
||||
import org.cef.network.CefRequest.TransitionType;
|
||||
import org.cef.network.CefResponse;
|
||||
import org.cef.network.CefURLRequest;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FocusTraversalPolicy;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* Client that owns a browser and renderer.
|
||||
*/
|
||||
public class CefClient extends CefClientHandler
|
||||
implements CefContextMenuHandler, CefDialogHandler, CefDisplayHandler, CefDownloadHandler,
|
||||
CefDragHandler, CefFocusHandler, CefJSDialogHandler, CefKeyboardHandler,
|
||||
CefLifeSpanHandler, CefLoadHandler, CefPrintHandler, CefRenderHandler,
|
||||
CefRequestHandler, CefWindowHandler {
|
||||
private HashMap<Integer, CefBrowser> browser_ = new HashMap<Integer, CefBrowser>();
|
||||
private CefContextMenuHandler contextMenuHandler_ = null;
|
||||
private CefDialogHandler dialogHandler_ = null;
|
||||
private CefDisplayHandler displayHandler_ = null;
|
||||
private CefDownloadHandler downloadHandler_ = null;
|
||||
private CefDragHandler dragHandler_ = null;
|
||||
private CefFocusHandler focusHandler_ = null;
|
||||
private CefJSDialogHandler jsDialogHandler_ = null;
|
||||
private CefKeyboardHandler keyboardHandler_ = null;
|
||||
private CefLifeSpanHandler lifeSpanHandler_ = null;
|
||||
private CefLoadHandler loadHandler_ = null;
|
||||
private CefPrintHandler printHandler_ = null;
|
||||
private CefRequestHandler requestHandler_ = null;
|
||||
private boolean isDisposed_ = false;
|
||||
private volatile CefBrowser focusedBrowser_ = null;
|
||||
private final PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (focusedBrowser_ != null) {
|
||||
Component browserUI = focusedBrowser_.getUIComponent();
|
||||
Object oldUI = evt.getOldValue();
|
||||
if (isPartOf(oldUI, browserUI)) {
|
||||
focusedBrowser_.setFocus(false);
|
||||
focusedBrowser_ = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The CTOR is only accessible within this package.
|
||||
* Use CefApp.createClient() to create an instance of
|
||||
* this class.
|
||||
* @see org.cef.CefApp.createClient()
|
||||
*/
|
||||
CefClient() throws UnsatisfiedLinkError {
|
||||
super();
|
||||
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(
|
||||
propertyChangeListener);
|
||||
}
|
||||
|
||||
private boolean isPartOf(Object obj, Component browserUI) {
|
||||
if (obj == browserUI) return true;
|
||||
if (obj instanceof Container) {
|
||||
Component childs[] = ((Container) obj).getComponents();
|
||||
for (Component child : childs) {
|
||||
return isPartOf(child, browserUI);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
isDisposed_ = true;
|
||||
cleanupBrowser(-1);
|
||||
}
|
||||
|
||||
// CefClientHandler
|
||||
|
||||
public CefBrowser createBrowser(
|
||||
String url, boolean isOffscreenRendered, boolean isTransparent) {
|
||||
return createBrowser(url, isOffscreenRendered, isTransparent, null);
|
||||
}
|
||||
|
||||
public CefBrowser createBrowser(String url, boolean isOffscreenRendered, boolean isTransparent,
|
||||
CefRequestContext context) {
|
||||
if (isDisposed_)
|
||||
throw new IllegalStateException("Can't create browser. CefClient is disposed");
|
||||
return CefBrowserFactory.create(this, url, isOffscreenRendered, isTransparent, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefBrowser getBrowser(int identifier) {
|
||||
synchronized (browser_) {
|
||||
return browser_.get(new Integer(identifier));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getAllBrowser() {
|
||||
synchronized (browser_) {
|
||||
return browser_.values().toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefContextMenuHandler getContextMenuHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefDialogHandler getDialogHandler() {
|
||||
return this;
|
||||
};
|
||||
|
||||
@Override
|
||||
protected CefDisplayHandler getDisplayHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefDownloadHandler getDownloadHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefDragHandler getDragHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefFocusHandler getFocusHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefJSDialogHandler getJSDialogHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefKeyboardHandler getKeyboardHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefLifeSpanHandler getLifeSpanHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefLoadHandler getLoadHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefPrintHandler getPrintHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefRenderHandler getRenderHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefRequestHandler getRequestHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefWindowHandler getWindowHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
// CefContextMenuHandler
|
||||
|
||||
public CefClient addContextMenuHandler(CefContextMenuHandler handler) {
|
||||
if (contextMenuHandler_ == null) contextMenuHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeContextMenuHandler() {
|
||||
contextMenuHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeContextMenu(
|
||||
CefBrowser browser, CefFrame frame, CefContextMenuParams params, CefMenuModel model) {
|
||||
if (contextMenuHandler_ != null && browser != null)
|
||||
contextMenuHandler_.onBeforeContextMenu(browser, frame, params, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextMenuCommand(CefBrowser browser, CefFrame frame,
|
||||
CefContextMenuParams params, int commandId, int eventFlags) {
|
||||
if (contextMenuHandler_ != null && browser != null)
|
||||
return contextMenuHandler_.onContextMenuCommand(
|
||||
browser, frame, params, commandId, eventFlags);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContextMenuDismissed(CefBrowser browser, CefFrame frame) {
|
||||
if (contextMenuHandler_ != null && browser != null)
|
||||
contextMenuHandler_.onContextMenuDismissed(browser, frame);
|
||||
}
|
||||
|
||||
// CefDialogHandler
|
||||
|
||||
public CefClient addDialogHandler(CefDialogHandler handler) {
|
||||
if (dialogHandler_ == null) dialogHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeDialogHandler() {
|
||||
dialogHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFileDialog(CefBrowser browser, FileDialogMode mode, String title,
|
||||
String defaultFilePath, Vector<String> acceptFilters, int selectedAcceptFilter,
|
||||
CefFileDialogCallback callback) {
|
||||
if (dialogHandler_ != null && browser != null) {
|
||||
return dialogHandler_.onFileDialog(browser, mode, title, defaultFilePath, acceptFilters,
|
||||
selectedAcceptFilter, callback);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// CefDisplayHandler
|
||||
|
||||
public CefClient addDisplayHandler(CefDisplayHandler handler) {
|
||||
if (displayHandler_ == null) displayHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeDisplayHandler() {
|
||||
displayHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddressChange(CefBrowser browser, CefFrame frame, String url) {
|
||||
if (displayHandler_ != null && browser != null)
|
||||
displayHandler_.onAddressChange(browser, frame, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleChange(CefBrowser browser, String title) {
|
||||
if (displayHandler_ != null && browser != null)
|
||||
displayHandler_.onTitleChange(browser, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTooltip(CefBrowser browser, String text) {
|
||||
if (displayHandler_ != null && browser != null) {
|
||||
return displayHandler_.onTooltip(browser, text);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusMessage(CefBrowser browser, String value) {
|
||||
if (displayHandler_ != null && browser != null) {
|
||||
displayHandler_.onStatusMessage(browser, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(CefBrowser browser, CefSettings.LogSeverity level,
|
||||
String message, String source, int line) {
|
||||
if (displayHandler_ != null && browser != null) {
|
||||
return displayHandler_.onConsoleMessage(browser, level, message, source, line);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCursorChange(CefBrowser browser, int cursorType) {
|
||||
if (browser == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (displayHandler_ != null && displayHandler_.onCursorChange(browser, cursorType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) {
|
||||
return realHandler.onCursorChange(browser, cursorType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CefDownloadHandler
|
||||
|
||||
public CefClient addDownloadHandler(CefDownloadHandler handler) {
|
||||
if (downloadHandler_ == null) downloadHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeDownloadHandler() {
|
||||
downloadHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeDownload(CefBrowser browser, CefDownloadItem downloadItem,
|
||||
String suggestedName, CefBeforeDownloadCallback callback) {
|
||||
if (downloadHandler_ != null && browser != null)
|
||||
downloadHandler_.onBeforeDownload(browser, downloadItem, suggestedName, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadUpdated(
|
||||
CefBrowser browser, CefDownloadItem downloadItem, CefDownloadItemCallback callback) {
|
||||
if (downloadHandler_ != null && browser != null)
|
||||
downloadHandler_.onDownloadUpdated(browser, downloadItem, callback);
|
||||
}
|
||||
|
||||
// CefDragHandler
|
||||
|
||||
public CefClient addDragHandler(CefDragHandler handler) {
|
||||
if (dragHandler_ == null) dragHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeDragHandler() {
|
||||
dragHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDragEnter(CefBrowser browser, CefDragData dragData, int mask) {
|
||||
if (dragHandler_ != null && browser != null)
|
||||
return dragHandler_.onDragEnter(browser, dragData, mask);
|
||||
return false;
|
||||
}
|
||||
|
||||
// CefFocusHandler
|
||||
|
||||
public CefClient addFocusHandler(CefFocusHandler handler) {
|
||||
if (focusHandler_ == null) focusHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeFocusHandler() {
|
||||
focusHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTakeFocus(CefBrowser browser, boolean next) {
|
||||
if (browser == null) return;
|
||||
|
||||
browser.setFocus(false);
|
||||
Container parent = browser.getUIComponent().getParent();
|
||||
if (parent != null) {
|
||||
FocusTraversalPolicy policy = null;
|
||||
while (parent != null) {
|
||||
policy = parent.getFocusTraversalPolicy();
|
||||
if (policy != null) break;
|
||||
parent = parent.getParent();
|
||||
}
|
||||
if (policy != null) {
|
||||
Component nextComp = next
|
||||
? policy.getComponentAfter(parent, browser.getUIComponent())
|
||||
: policy.getComponentBefore(parent, browser.getUIComponent());
|
||||
if (nextComp == null) {
|
||||
policy.getDefaultComponent(parent).requestFocus();
|
||||
} else {
|
||||
nextComp.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
focusedBrowser_ = null;
|
||||
if (focusHandler_ != null) focusHandler_.onTakeFocus(browser, next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSetFocus(final CefBrowser browser, FocusSource source) {
|
||||
if (browser == null) return false;
|
||||
|
||||
boolean alreadyHandled = false;
|
||||
if (focusHandler_ != null) alreadyHandled = focusHandler_.onSetFocus(browser, source);
|
||||
return alreadyHandled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGotFocus(CefBrowser browser) {
|
||||
if (browser == null) return;
|
||||
|
||||
focusedBrowser_ = browser;
|
||||
browser.setFocus(true);
|
||||
if (focusHandler_ != null) focusHandler_.onGotFocus(browser);
|
||||
}
|
||||
|
||||
// CefJSDialogHandler
|
||||
|
||||
public CefClient addJSDialogHandler(CefJSDialogHandler handler) {
|
||||
if (jsDialogHandler_ == null) jsDialogHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeJSDialogHandler() {
|
||||
jsDialogHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onJSDialog(CefBrowser browser, String origin_url, JSDialogType dialog_type,
|
||||
String message_text, String default_prompt_text, CefJSDialogCallback callback,
|
||||
BoolRef suppress_message) {
|
||||
if (jsDialogHandler_ != null && browser != null)
|
||||
return jsDialogHandler_.onJSDialog(browser, origin_url, dialog_type, message_text,
|
||||
default_prompt_text, callback, suppress_message);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBeforeUnloadDialog(CefBrowser browser, String message_text, boolean is_reload,
|
||||
CefJSDialogCallback callback) {
|
||||
if (jsDialogHandler_ != null && browser != null)
|
||||
return jsDialogHandler_.onBeforeUnloadDialog(
|
||||
browser, message_text, is_reload, callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResetDialogState(CefBrowser browser) {
|
||||
if (jsDialogHandler_ != null && browser != null)
|
||||
jsDialogHandler_.onResetDialogState(browser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogClosed(CefBrowser browser) {
|
||||
if (jsDialogHandler_ != null && browser != null) jsDialogHandler_.onDialogClosed(browser);
|
||||
}
|
||||
|
||||
// CefKeyboardHandler
|
||||
|
||||
public CefClient addKeyboardHandler(CefKeyboardHandler handler) {
|
||||
if (keyboardHandler_ == null) keyboardHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeKeyboardHandler() {
|
||||
keyboardHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreKeyEvent(
|
||||
CefBrowser browser, CefKeyEvent event, BoolRef is_keyboard_shortcut) {
|
||||
if (keyboardHandler_ != null && browser != null)
|
||||
return keyboardHandler_.onPreKeyEvent(browser, event, is_keyboard_shortcut);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyEvent(CefBrowser browser, CefKeyEvent event) {
|
||||
if (keyboardHandler_ != null && browser != null)
|
||||
return keyboardHandler_.onKeyEvent(browser, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
// CefLifeSpanHandler
|
||||
|
||||
public CefClient addLifeSpanHandler(CefLifeSpanHandler handler) {
|
||||
if (lifeSpanHandler_ == null) lifeSpanHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeLifeSpanHandler() {
|
||||
lifeSpanHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBeforePopup(
|
||||
CefBrowser browser, CefFrame frame, String target_url, String target_frame_name) {
|
||||
if (isDisposed_) return true;
|
||||
if (lifeSpanHandler_ != null && browser != null)
|
||||
return lifeSpanHandler_.onBeforePopup(browser, frame, target_url, target_frame_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAfterCreated(CefBrowser browser) {
|
||||
if (browser == null) return;
|
||||
|
||||
// keep browser reference
|
||||
Integer identifier = browser.getIdentifier();
|
||||
synchronized (browser_) {
|
||||
browser_.put(identifier, browser);
|
||||
}
|
||||
if (lifeSpanHandler_ != null) lifeSpanHandler_.onAfterCreated(browser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAfterParentChanged(CefBrowser browser) {
|
||||
if (browser == null) return;
|
||||
if (lifeSpanHandler_ != null) lifeSpanHandler_.onAfterParentChanged(browser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doClose(CefBrowser browser) {
|
||||
if (browser == null) return false;
|
||||
if (lifeSpanHandler_ != null) return lifeSpanHandler_.doClose(browser);
|
||||
return browser.doClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeClose(CefBrowser browser) {
|
||||
if (browser == null) return;
|
||||
if (lifeSpanHandler_ != null) lifeSpanHandler_.onBeforeClose(browser);
|
||||
browser.onBeforeClose();
|
||||
|
||||
// remove browser reference
|
||||
cleanupBrowser(browser.getIdentifier());
|
||||
}
|
||||
|
||||
private void cleanupBrowser(int identifier) {
|
||||
synchronized (browser_) {
|
||||
if (identifier >= 0) {
|
||||
// Remove the specific browser that closed.
|
||||
browser_.remove(identifier);
|
||||
} else if (!browser_.isEmpty()) {
|
||||
// Close all browsers.
|
||||
Collection<CefBrowser> browserList = browser_.values();
|
||||
for (CefBrowser browser : browserList) {
|
||||
browser.close(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (browser_.isEmpty() && isDisposed_) {
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(
|
||||
propertyChangeListener);
|
||||
removeContextMenuHandler(this);
|
||||
removeDialogHandler(this);
|
||||
removeDisplayHandler(this);
|
||||
removeDownloadHandler(this);
|
||||
removeDragHandler(this);
|
||||
removeFocusHandler(this);
|
||||
removeJSDialogHandler(this);
|
||||
removeKeyboardHandler(this);
|
||||
removeLifeSpanHandler(this);
|
||||
removeLoadHandler(this);
|
||||
removePrintHandler(this);
|
||||
removeRenderHandler(this);
|
||||
removeRequestHandler(this);
|
||||
removeWindowHandler(this);
|
||||
super.dispose();
|
||||
|
||||
CefApp.getInstance().clientWasDisposed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CefLoadHandler
|
||||
|
||||
public CefClient addLoadHandler(CefLoadHandler handler) {
|
||||
if (loadHandler_ == null) loadHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeLoadHandler() {
|
||||
loadHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingStateChange(
|
||||
CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) {
|
||||
if (loadHandler_ != null && browser != null)
|
||||
loadHandler_.onLoadingStateChange(browser, isLoading, canGoBack, canGoForward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadStart(CefBrowser browser, CefFrame frame, TransitionType transitionType) {
|
||||
if (loadHandler_ != null && browser != null)
|
||||
loadHandler_.onLoadStart(browser, frame, transitionType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) {
|
||||
if (loadHandler_ != null && browser != null)
|
||||
loadHandler_.onLoadEnd(browser, frame, httpStatusCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode,
|
||||
String errorText, String failedUrl) {
|
||||
if (loadHandler_ != null && browser != null)
|
||||
loadHandler_.onLoadError(browser, frame, errorCode, errorText, failedUrl);
|
||||
}
|
||||
|
||||
// CefPrintHandler
|
||||
|
||||
public CefClient addPrintHandler(CefPrintHandler handler) {
|
||||
if (printHandler_ == null) printHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removePrintHandler() {
|
||||
printHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintStart(CefBrowser browser) {
|
||||
if (printHandler_ != null && browser != null) printHandler_.onPrintStart(browser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintSettings(
|
||||
CefBrowser browser, CefPrintSettings settings, boolean getDefaults) {
|
||||
if (printHandler_ != null && browser != null)
|
||||
printHandler_.onPrintSettings(browser, settings, getDefaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrintDialog(
|
||||
CefBrowser browser, boolean hasSelection, CefPrintDialogCallback callback) {
|
||||
if (printHandler_ != null && browser != null)
|
||||
return printHandler_.onPrintDialog(browser, hasSelection, callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrintJob(CefBrowser browser, String documentName, String pdfFilePath,
|
||||
CefPrintJobCallback callback) {
|
||||
if (printHandler_ != null && browser != null)
|
||||
return printHandler_.onPrintJob(browser, documentName, pdfFilePath, callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintReset(CefBrowser browser) {
|
||||
if (printHandler_ != null && browser != null) printHandler_.onPrintReset(browser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPdfPaperSize(CefBrowser browser, int deviceUnitsPerInch) {
|
||||
if (printHandler_ != null && browser != null)
|
||||
return printHandler_.getPdfPaperSize(browser, deviceUnitsPerInch);
|
||||
return null;
|
||||
}
|
||||
|
||||
// CefMessageRouter
|
||||
|
||||
@Override
|
||||
public synchronized void addMessageRouter(CefMessageRouter messageRouter) {
|
||||
super.addMessageRouter(messageRouter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeMessageRouter(CefMessageRouter messageRouter) {
|
||||
super.removeMessageRouter(messageRouter);
|
||||
}
|
||||
|
||||
// CefRenderHandler
|
||||
|
||||
@Override
|
||||
public Rectangle getViewRect(CefBrowser browser) {
|
||||
if (browser == null) return new Rectangle(0, 0, 0, 0);
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) return realHandler.getViewRect(browser);
|
||||
return new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getScreenPoint(CefBrowser browser, Point viewPoint) {
|
||||
if (browser == null) return new Point(0, 0);
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) return realHandler.getScreenPoint(browser, viewPoint);
|
||||
return new Point(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopupShow(CefBrowser browser, boolean show) {
|
||||
if (browser == null) return;
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) realHandler.onPopupShow(browser, show);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopupSize(CefBrowser browser, Rectangle size) {
|
||||
if (browser == null) return;
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) realHandler.onPopupSize(browser, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
|
||||
ByteBuffer buffer, int width, int height) {
|
||||
if (browser == null) return;
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null)
|
||||
realHandler.onPaint(browser, popup, dirtyRects, buffer, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startDragging(CefBrowser browser, CefDragData dragData, int mask, int x, int y) {
|
||||
if (browser == null) return false;
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) return realHandler.startDragging(browser, dragData, mask, x, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDragCursor(CefBrowser browser, int operation) {
|
||||
if (browser == null) return;
|
||||
|
||||
CefRenderHandler realHandler = browser.getRenderHandler();
|
||||
if (realHandler != null) realHandler.updateDragCursor(browser, operation);
|
||||
}
|
||||
|
||||
// CefRequestHandler
|
||||
|
||||
public CefClient addRequestHandler(CefRequestHandler handler) {
|
||||
if (requestHandler_ == null) requestHandler_ = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeRequestHandler() {
|
||||
requestHandler_ = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame, CefRequest request,
|
||||
boolean user_gesture, boolean is_redirect) {
|
||||
if (requestHandler_ != null && browser != null)
|
||||
return requestHandler_.onBeforeBrowse(
|
||||
browser, frame, request, user_gesture, is_redirect);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOpenURLFromTab(
|
||||
CefBrowser browser, CefFrame frame, String target_url, boolean user_gesture) {
|
||||
if (isDisposed_) return true;
|
||||
if (requestHandler_ != null && browser != null)
|
||||
return requestHandler_.onOpenURLFromTab(browser, frame, target_url, user_gesture);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefResourceRequestHandler getResourceRequestHandler(CefBrowser browser, CefFrame frame,
|
||||
CefRequest request, boolean isNavigation, boolean isDownload, String requestInitiator,
|
||||
BoolRef disableDefaultHandling) {
|
||||
if (requestHandler_ != null && browser != null) {
|
||||
return requestHandler_.getResourceRequestHandler(browser, frame, request, isNavigation,
|
||||
isDownload, requestInitiator, disableDefaultHandling);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAuthCredentials(CefBrowser browser, String origin_url, boolean isProxy,
|
||||
String host, int port, String realm, String scheme, CefAuthCallback callback) {
|
||||
if (requestHandler_ != null && browser != null)
|
||||
return requestHandler_.getAuthCredentials(
|
||||
browser, origin_url, isProxy, host, port, realm, scheme, callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQuotaRequest(
|
||||
CefBrowser browser, String origin_url, long new_size, CefCallback callback) {
|
||||
if (requestHandler_ != null && browser != null)
|
||||
return requestHandler_.onQuotaRequest(browser, origin_url, new_size, callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCertificateError(
|
||||
CefBrowser browser, ErrorCode cert_error, String request_url, CefCallback callback) {
|
||||
if (requestHandler_ != null)
|
||||
return requestHandler_.onCertificateError(browser, cert_error, request_url, callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderProcessTerminated(CefBrowser browser, TerminationStatus status) {
|
||||
if (requestHandler_ != null) requestHandler_.onRenderProcessTerminated(browser, status);
|
||||
}
|
||||
|
||||
// CefWindowHandler
|
||||
|
||||
@Override
|
||||
public Rectangle getRect(CefBrowser browser) {
|
||||
if (browser == null) return new Rectangle(0, 0, 0, 0);
|
||||
|
||||
CefWindowHandler realHandler = browser.getWindowHandler();
|
||||
if (realHandler != null) return realHandler.getRect(browser);
|
||||
return new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseEvent(
|
||||
CefBrowser browser, int event, int screenX, int screenY, int modifier, int button) {
|
||||
if (browser == null) return;
|
||||
|
||||
CefWindowHandler realHandler = browser.getWindowHandler();
|
||||
if (realHandler != null)
|
||||
realHandler.onMouseEvent(browser, event, screenX, screenY, modifier, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScreenInfo(CefBrowser arg0, CefScreenInfo arg1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef;
|
||||
|
||||
/**
|
||||
* Initialization settings. Specify NULL or 0 to get the recommended default
|
||||
* values. Many of these and other settings can also configured using command-
|
||||
* line switches.
|
||||
*/
|
||||
public class CefSettings {
|
||||
/**
|
||||
* Log severity levels.
|
||||
*/
|
||||
public enum LogSeverity {
|
||||
/**
|
||||
* Default logging (currently INFO logging).
|
||||
*/
|
||||
LOGSEVERITY_DEFAULT,
|
||||
|
||||
/**
|
||||
* Verbose logging.
|
||||
*/
|
||||
LOGSEVERITY_VERBOSE,
|
||||
|
||||
/**
|
||||
* INFO logging.
|
||||
*/
|
||||
LOGSEVERITY_INFO,
|
||||
|
||||
/**
|
||||
* WARNING logging.
|
||||
*/
|
||||
LOGSEVERITY_WARNING,
|
||||
|
||||
/**
|
||||
* ERROR logging.
|
||||
*/
|
||||
LOGSEVERITY_ERROR,
|
||||
|
||||
/**
|
||||
* FATAL logging.
|
||||
*/
|
||||
LOGSEVERITY_FATAL,
|
||||
|
||||
/**
|
||||
* Completely disable logging.
|
||||
*/
|
||||
LOGSEVERITY_DISABLE
|
||||
}
|
||||
|
||||
/**
|
||||
* 32-bit ARGB color value, not premultiplied. The color components are always
|
||||
* in a known order. Equivalent to the SkColor type.
|
||||
*/
|
||||
public class ColorType {
|
||||
private long color_value = 0;
|
||||
|
||||
private ColorType() {}
|
||||
|
||||
public ColorType(int alpha, int red, int green, int blue) {
|
||||
color_value = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
|
||||
}
|
||||
|
||||
public long getColor() {
|
||||
return color_value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorType clone() {
|
||||
ColorType res = new ColorType();
|
||||
res.color_value = this.color_value;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// MacOS specific settings
|
||||
public String framework_dir_path = null;
|
||||
public String main_bundle_path = null;
|
||||
|
||||
/**
|
||||
* The path to a separate executable that will be launched for sub-processes.
|
||||
* By default the browser process executable is used. See the comments on
|
||||
* CefExecuteProcess() for details. Also configurable using the
|
||||
* "browser-subprocess-path" command-line switch.
|
||||
*/
|
||||
public String browser_subprocess_path = null;
|
||||
|
||||
/**
|
||||
* Set to true to enable windowless (off-screen) rendering support. Do not
|
||||
* enable this value if the application does not use windowless rendering as
|
||||
* it may reduce rendering performance on some systems.
|
||||
*/
|
||||
public boolean windowless_rendering_enabled = true;
|
||||
|
||||
/**
|
||||
* Set to true to disable configuration of browser process features using
|
||||
* standard CEF and Chromium command-line arguments. Configuration can still
|
||||
* be specified using CEF data structures or via the
|
||||
* CefApp::OnBeforeCommandLineProcessing() method.
|
||||
*/
|
||||
public boolean command_line_args_disabled = false;
|
||||
|
||||
/**
|
||||
* The location where cache data will be stored on disk. If empty an in-memory
|
||||
* cache will be used for some features and a temporary disk cache for others.
|
||||
* HTML5 databases such as localStorage will only persist across sessions if a
|
||||
* cache path is specified.
|
||||
*/
|
||||
public String cache_path = null;
|
||||
|
||||
/**
|
||||
* To persist session cookies (cookies without an expiry date or validity
|
||||
* interval) by default when using the global cookie manager set this value to
|
||||
* true. Session cookies are generally intended to be transient and most Web
|
||||
* browsers do not persist them. A |cache_path| value must also be specified to
|
||||
* enable this feature. Also configurable using the "persist-session-cookies"
|
||||
* command-line switch.
|
||||
*/
|
||||
public boolean persist_session_cookies = false;
|
||||
|
||||
/**
|
||||
* Value that will be returned as the User-Agent HTTP header. If empty the
|
||||
* default User-Agent string will be used. Also configurable using the
|
||||
* "user-agent" command-line switch.
|
||||
*/
|
||||
public String user_agent = null;
|
||||
|
||||
/**
|
||||
* Value that will be inserted as the product portion of the default
|
||||
* User-Agent string. If empty the Chromium product version will be used. If
|
||||
* |userAgent| is specified this value will be ignored. Also configurable
|
||||
* using the "product-version" command-line switch.
|
||||
*/
|
||||
public String product_version = null;
|
||||
|
||||
/**
|
||||
* The locale string that will be passed to Blink. If empty the default
|
||||
* locale of "en-US" will be used. This value is ignored on Linux where locale
|
||||
* is determined using environment variable parsing with the precedence order:
|
||||
* LANGUAGE, LC_ALL, LC_MESSAGES and LANG. Also configurable using the "lang"
|
||||
* command-line switch.
|
||||
*/
|
||||
public String locale = null;
|
||||
|
||||
/**
|
||||
* The directory and file name to use for the debug log. If empty, the
|
||||
* default name of "debug.log" will be used and the file will be written
|
||||
* to the application directory. Also configurable using the "log-file"
|
||||
* command-line switch.
|
||||
*/
|
||||
public String log_file = null;
|
||||
|
||||
/**
|
||||
* The log severity. Only messages of this severity level or higher will be
|
||||
* logged. Also configurable using the "log-severity" command-line switch with
|
||||
* a value of "verbose", "info", "warning", "error", "error-report" or
|
||||
* "disable".
|
||||
*/
|
||||
public LogSeverity log_severity = LogSeverity.LOGSEVERITY_DEFAULT;
|
||||
|
||||
/**
|
||||
* Custom flags that will be used when initializing the V8 JavaScript engine.
|
||||
* The consequences of using custom flags may not be well tested. Also
|
||||
* configurable using the "js-flags" command-line switch.
|
||||
*/
|
||||
public String javascript_flags = null;
|
||||
|
||||
/**
|
||||
* The fully qualified path for the resources directory. If this value is
|
||||
* empty the cef.pak and/or devtools_resources.pak files must be located in
|
||||
* the module directory on Windows/Linux or the app bundle Resources directory
|
||||
* on Mac OS X. Also configurable using the "resources-dir-path" command-line
|
||||
* switch.
|
||||
*/
|
||||
public String resources_dir_path = null;
|
||||
|
||||
/**
|
||||
* The fully qualified path for the locales directory. If this value is empty
|
||||
* the locales directory must be located in the module directory. This value
|
||||
* is ignored on Mac OS X where pack files are always loaded from the app
|
||||
* bundle Resources directory. Also configurable using the "locales-dir-path"
|
||||
* command-line switch.
|
||||
*/
|
||||
public String locales_dir_path = null;
|
||||
|
||||
/**
|
||||
* Set to true to disable loading of pack files for resources and locales.
|
||||
* A resource bundle handler must be provided for the browser and render
|
||||
* processes via CefApp::GetResourceBundleHandler() if loading of pack files
|
||||
* is disabled. Also configurable using the "disable-pack-loading" command-
|
||||
* line switch.
|
||||
*/
|
||||
public boolean pack_loading_disabled = false;
|
||||
|
||||
/**
|
||||
* Set to a value between 1024 and 65535 to enable remote debugging on the
|
||||
* specified port. For example, if 8080 is specified the remote debugging URL
|
||||
* will be http: *localhost:8080. CEF can be remotely debugged from any CEF or
|
||||
* Chrome browser window. Also configurable using the "remote-debugging-port"
|
||||
* command-line switch.
|
||||
*/
|
||||
public int remote_debugging_port = 0;
|
||||
|
||||
/**
|
||||
* The number of stack trace frames to capture for uncaught exceptions.
|
||||
* Specify a positive value to enable the CefV8ContextHandler::
|
||||
* OnUncaughtException() callback. Specify 0 (default value) and
|
||||
* OnUncaughtException() will not be called. Also configurable using the
|
||||
* "uncaught-exception-stack-size" command-line switch.
|
||||
*/
|
||||
public int uncaught_exception_stack_size = 0;
|
||||
|
||||
/**
|
||||
* Set to true to ignore errors related to invalid SSL certificates.
|
||||
* Enabling this setting can lead to potential security vulnerabilities like
|
||||
* "man in the middle" attacks. Applications that load content from the
|
||||
* internet should not enable this setting. Also configurable using the
|
||||
* "ignore-certificate-errors" command-line switch.
|
||||
*/
|
||||
public boolean ignore_certificate_errors = false;
|
||||
|
||||
/**
|
||||
* Opaque background color used for accelerated content. By default the
|
||||
* background color will be white. Only the RGB compontents of the specified
|
||||
* value will be used. The alpha component must greater than 0 to enable use
|
||||
* of the background color but will be otherwise ignored.
|
||||
*/
|
||||
public ColorType background_color = null;
|
||||
|
||||
public CefSettings() {}
|
||||
|
||||
@Override
|
||||
public CefSettings clone() {
|
||||
CefSettings tmp = new CefSettings();
|
||||
tmp.framework_dir_path = framework_dir_path; // for MacOS
|
||||
tmp.main_bundle_path = main_bundle_path; // for MacOS
|
||||
tmp.browser_subprocess_path = browser_subprocess_path;
|
||||
tmp.windowless_rendering_enabled = windowless_rendering_enabled;
|
||||
tmp.command_line_args_disabled = command_line_args_disabled;
|
||||
tmp.cache_path = cache_path;
|
||||
tmp.persist_session_cookies = persist_session_cookies;
|
||||
tmp.user_agent = user_agent;
|
||||
tmp.product_version = product_version;
|
||||
tmp.locale = locale;
|
||||
tmp.log_file = log_file;
|
||||
tmp.log_severity = log_severity;
|
||||
tmp.javascript_flags = javascript_flags;
|
||||
tmp.resources_dir_path = resources_dir_path;
|
||||
tmp.locales_dir_path = locales_dir_path;
|
||||
tmp.pack_loading_disabled = pack_loading_disabled;
|
||||
tmp.remote_debugging_port = remote_debugging_port;
|
||||
tmp.uncaught_exception_stack_size = uncaught_exception_stack_size;
|
||||
tmp.ignore_certificate_errors = ignore_certificate_errors;
|
||||
if (background_color != null) tmp.background_color = background_color.clone();
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef;
|
||||
|
||||
public class OS {
|
||||
private static enum OSType {
|
||||
OSUndefined,
|
||||
OSLinux,
|
||||
OSWindows,
|
||||
OSMacintosh,
|
||||
OSUnknown,
|
||||
}
|
||||
;
|
||||
private static OSType osType = OSType.OSUndefined;
|
||||
|
||||
public static final boolean isWindows() {
|
||||
return getOSType() == OSType.OSWindows;
|
||||
}
|
||||
|
||||
public static final boolean isMacintosh() {
|
||||
return getOSType() == OSType.OSMacintosh;
|
||||
}
|
||||
|
||||
public static final boolean isLinux() {
|
||||
return getOSType() == OSType.OSLinux;
|
||||
}
|
||||
|
||||
private static final OSType getOSType() {
|
||||
if (osType == OSType.OSUndefined) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.startsWith("windows"))
|
||||
osType = OSType.OSWindows;
|
||||
else if (os.startsWith("linux"))
|
||||
osType = OSType.OSLinux;
|
||||
else if (os.startsWith("mac"))
|
||||
osType = OSType.OSMacintosh;
|
||||
else
|
||||
osType = OSType.OSUnknown;
|
||||
}
|
||||
return osType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
package org.cef;
|
||||
|
||||
/**
|
||||
* To allow customization of System.load() calls by supplying a different
|
||||
* implementation. You'll want to call <code>setLoader</code> with your custom
|
||||
* implementation before calling into any other CEF classes which then in turn
|
||||
* will start triggering libraries to be loaded at runtime.
|
||||
*/
|
||||
public class SystemBootstrap {
|
||||
/**
|
||||
* Simple interface for how a library by name should be loaded.
|
||||
*/
|
||||
static public interface Loader { public void loadLibrary(String libname); }
|
||||
|
||||
/**
|
||||
* Default implementation is to call System.loadLibrary
|
||||
*/
|
||||
static private Loader loader_ = new Loader() {
|
||||
@Override
|
||||
public void loadLibrary(String libname) {
|
||||
System.loadLibrary(libname);
|
||||
}
|
||||
};
|
||||
|
||||
static public void setLoader(Loader loader) {
|
||||
if (loader == null) {
|
||||
throw new NullPointerException("Loader cannot be null");
|
||||
}
|
||||
loader_ = loader;
|
||||
}
|
||||
|
||||
static public void loadLibrary(String libname) {
|
||||
loader_.loadLibrary(libname);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.CefClient;
|
||||
import org.cef.callback.CefPdfPrintCallback;
|
||||
import org.cef.callback.CefRunFileDialogCallback;
|
||||
import org.cef.callback.CefStringVisitor;
|
||||
import org.cef.handler.CefDialogHandler.FileDialogMode;
|
||||
import org.cef.handler.CefRenderHandler;
|
||||
import org.cef.handler.CefWindowHandler;
|
||||
import org.cef.misc.CefPdfPrintSettings;
|
||||
import org.cef.network.CefRequest;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Point;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Interface representing a browser.
|
||||
*/
|
||||
public interface CefBrowser {
|
||||
/**
|
||||
* Call to immediately create the underlying browser object. By default the
|
||||
* browser object will be created when the parent container is displayed for
|
||||
* the first time.
|
||||
*/
|
||||
public void createImmediately();
|
||||
|
||||
/**
|
||||
* Get the underlying UI component (e.g. java.awt.Canvas).
|
||||
* @return The underlying UI component.
|
||||
*/
|
||||
public Component getUIComponent();
|
||||
|
||||
/**
|
||||
* Get the client associated with this browser.
|
||||
* @return The browser client.
|
||||
*/
|
||||
public CefClient getClient();
|
||||
|
||||
/**
|
||||
* Get an implementation of CefRenderHandler if any.
|
||||
* @return An instance of CefRenderHandler or null.
|
||||
*/
|
||||
public CefRenderHandler getRenderHandler();
|
||||
|
||||
/**
|
||||
* Get an implementation of CefWindowHandler if any.
|
||||
* @return An instance of CefWindowHandler or null.
|
||||
*/
|
||||
public CefWindowHandler getWindowHandler();
|
||||
|
||||
//
|
||||
// The following methods are forwarded to CefBrowser.
|
||||
//
|
||||
|
||||
/**
|
||||
* Tests if the browser can navigate backwards.
|
||||
* @return true if the browser can navigate backwards.
|
||||
*/
|
||||
public boolean canGoBack();
|
||||
|
||||
/**
|
||||
* Go back.
|
||||
*/
|
||||
public void goBack();
|
||||
|
||||
/**
|
||||
* Tests if the browser can navigate forwards.
|
||||
* @return true if the browser can navigate forwards.
|
||||
*/
|
||||
public boolean canGoForward();
|
||||
|
||||
/**
|
||||
* Go forward.
|
||||
*/
|
||||
public void goForward();
|
||||
|
||||
/**
|
||||
* Tests if the browser is currently loading.
|
||||
* @return true if the browser is currently loading.
|
||||
*/
|
||||
public boolean isLoading();
|
||||
|
||||
/**
|
||||
* Reload the current page.
|
||||
*/
|
||||
public void reload();
|
||||
|
||||
/**
|
||||
* Reload the current page ignoring any cached data.
|
||||
*/
|
||||
public void reloadIgnoreCache();
|
||||
|
||||
/**
|
||||
* Stop loading the page.
|
||||
*/
|
||||
public void stopLoad();
|
||||
|
||||
/**
|
||||
* Returns the unique browser identifier.
|
||||
* @return The browser identifier
|
||||
*/
|
||||
public int getIdentifier();
|
||||
|
||||
/**
|
||||
* Returns the main (top-level) frame for the browser window.
|
||||
* @return The main frame
|
||||
*/
|
||||
public CefFrame getMainFrame();
|
||||
|
||||
/**
|
||||
* Returns the focused frame for the browser window.
|
||||
* @return The focused frame
|
||||
*/
|
||||
public CefFrame getFocusedFrame();
|
||||
|
||||
/**
|
||||
* Returns the frame with the specified identifier, or NULL if not found.
|
||||
* @param identifier The unique frame identifier
|
||||
* @return The frame or NULL if not found
|
||||
*/
|
||||
public CefFrame getFrame(long identifier);
|
||||
|
||||
/**
|
||||
* Returns the frame with the specified name, or NULL if not found.
|
||||
* @param name The specified name
|
||||
* @return The frame or NULL if not found
|
||||
*/
|
||||
public CefFrame getFrame(String name);
|
||||
|
||||
/**
|
||||
* Returns the identifiers of all existing frames.
|
||||
* @return All identifiers of existing frames.
|
||||
*/
|
||||
public Vector<Long> getFrameIdentifiers();
|
||||
|
||||
/**
|
||||
* Returns the names of all existing frames.
|
||||
* @return The names of all existing frames.
|
||||
*/
|
||||
public Vector<String> getFrameNames();
|
||||
|
||||
/**
|
||||
* Returns the number of frames that currently exist.
|
||||
* @return The number of frames
|
||||
*/
|
||||
public int getFrameCount();
|
||||
|
||||
/**
|
||||
* Tests if the window is a popup window.
|
||||
* @return true if the window is a popup window.
|
||||
*/
|
||||
public boolean isPopup();
|
||||
|
||||
/**
|
||||
* Tests if a document has been loaded in the browser.
|
||||
* @return true if a document has been loaded in the browser.
|
||||
*/
|
||||
public boolean hasDocument();
|
||||
|
||||
//
|
||||
// The following methods are forwarded to the mainFrame.
|
||||
//
|
||||
|
||||
/**
|
||||
* Save this frame's HTML source to a temporary file and open it in the
|
||||
* default text viewing application. This method can only be called from the
|
||||
* browser process.
|
||||
*/
|
||||
public void viewSource();
|
||||
|
||||
/**
|
||||
* Retrieve this frame's HTML source as a string sent to the specified
|
||||
* visitor.
|
||||
*
|
||||
* @param visitor
|
||||
*/
|
||||
public void getSource(CefStringVisitor visitor);
|
||||
|
||||
/**
|
||||
* Retrieve this frame's display text as a string sent to the specified
|
||||
* visitor.
|
||||
*
|
||||
* @param visitor
|
||||
*/
|
||||
public void getText(CefStringVisitor visitor);
|
||||
|
||||
/**
|
||||
* Load the request represented by the request object.
|
||||
*
|
||||
* @param request The request object.
|
||||
*/
|
||||
public void loadRequest(CefRequest request);
|
||||
|
||||
/**
|
||||
* Load the specified URL in the main frame.
|
||||
* @param url The URL to load.
|
||||
*/
|
||||
public void loadURL(String url);
|
||||
|
||||
/**
|
||||
* Execute a string of JavaScript code in this frame. The url
|
||||
* parameter is the URL where the script in question can be found, if any.
|
||||
* The renderer may request this URL to show the developer the source of the
|
||||
* error. The line parameter is the base line number to use for error
|
||||
* reporting.
|
||||
*
|
||||
* @param code The code to be executed.
|
||||
* @param url The URL where the script in question can be found.
|
||||
* @param line The base line number to use for error reporting.
|
||||
*/
|
||||
public void executeJavaScript(String code, String url, int line);
|
||||
|
||||
/**
|
||||
* Emits the URL currently loaded in this frame.
|
||||
* @return the URL currently loaded in this frame.
|
||||
*/
|
||||
public String getURL();
|
||||
|
||||
// The following methods are forwarded to CefBrowserHost.
|
||||
|
||||
/**
|
||||
* Request that the browser close.
|
||||
* @param force force the close.
|
||||
*/
|
||||
public void close(boolean force);
|
||||
|
||||
/**
|
||||
* Allow the browser to close.
|
||||
*/
|
||||
public void setCloseAllowed();
|
||||
|
||||
/**
|
||||
* Called from CefClient.doClose.
|
||||
*/
|
||||
public boolean doClose();
|
||||
|
||||
/**
|
||||
* Called from CefClient.onBeforeClose.
|
||||
*/
|
||||
public void onBeforeClose();
|
||||
|
||||
/**
|
||||
* Set or remove keyboard focus to/from the browser window.
|
||||
* @param enable set to true to give the focus to the browser
|
||||
**/
|
||||
public void setFocus(boolean enable);
|
||||
|
||||
/**
|
||||
* Set whether the window containing the browser is visible
|
||||
* (minimized/unminimized, app hidden/unhidden, etc). Only used on Mac OS X.
|
||||
* @param visible
|
||||
*/
|
||||
public void setWindowVisibility(boolean visible);
|
||||
|
||||
/**
|
||||
* Get the current zoom level. The default zoom level is 0.0.
|
||||
* @return The current zoom level.
|
||||
*/
|
||||
public double getZoomLevel();
|
||||
|
||||
/**
|
||||
* Change the zoom level to the specified value. Specify 0.0 to reset the
|
||||
* zoom level.
|
||||
*
|
||||
* @param zoomLevel The zoom level to be set.
|
||||
*/
|
||||
public void setZoomLevel(double zoomLevel);
|
||||
|
||||
/**
|
||||
* Call to run a file chooser dialog. Only a single file chooser dialog may be
|
||||
* pending at any given time.The dialog will be initiated asynchronously on
|
||||
* the UI thread.
|
||||
*
|
||||
* @param mode represents the type of dialog to display.
|
||||
* @param title to be used for the dialog and may be empty to show the
|
||||
* default title ("Open" or "Save" depending on the mode).
|
||||
* @param defaultFilePath is the path with optional directory and/or file name
|
||||
* component that should be initially selected in the dialog.
|
||||
* @param acceptFilters are used to restrict the selectable file types and may
|
||||
* any combination of (a) valid lower-cased MIME types (e.g. "text/*" or
|
||||
* "image/*"), (b) individual file extensions (e.g. ".txt" or ".png"), or (c)
|
||||
* combined description and file extension delimited using "|" and ";" (e.g.
|
||||
* "Image Types|.png;.gif;.jpg").
|
||||
* @param selectedAcceptFilter is the 0-based index of the filter that should
|
||||
* be selected by default.
|
||||
* @param callback will be executed after the dialog is dismissed or
|
||||
* immediately if another dialog is already pending.
|
||||
*/
|
||||
public void runFileDialog(FileDialogMode mode, String title, String defaultFilePath,
|
||||
Vector<String> acceptFilters, int selectedAcceptFilter,
|
||||
CefRunFileDialogCallback callback);
|
||||
|
||||
/**
|
||||
* Download the file at url using CefDownloadHandler.
|
||||
*
|
||||
* @param url URL to download that file.
|
||||
*/
|
||||
public void startDownload(String url);
|
||||
|
||||
/**
|
||||
* Print the current browser contents.
|
||||
*/
|
||||
public void print();
|
||||
|
||||
/**
|
||||
* Print the current browser contents to a PDF.
|
||||
*
|
||||
* @param path The path of the file to write to (will be overwritten if it
|
||||
* already exists). Cannot be null.
|
||||
* @param settings The pdf print settings to use. If null then defaults
|
||||
* will be used.
|
||||
* @param callback Called when the pdf print job has completed.
|
||||
*/
|
||||
public void printToPDF(String path, CefPdfPrintSettings settings, CefPdfPrintCallback callback);
|
||||
|
||||
/**
|
||||
* Search for some kind of text on the page.
|
||||
*
|
||||
* @param searchText to be searched for.
|
||||
* @param forward indicates whether to search forward or backward within the page.
|
||||
* @param matchCase indicates whether the search should be case-sensitive.
|
||||
* @param findNext indicates whether this is the first request or a follow-up.
|
||||
*/
|
||||
public void find(String searchText, boolean forward, boolean matchCase, boolean findNext);
|
||||
|
||||
/**
|
||||
* Cancel all searches that are currently going on.
|
||||
* @param clearSelection Set to true to reset selection.
|
||||
*/
|
||||
public void stopFinding(boolean clearSelection);
|
||||
|
||||
/**
|
||||
* Get an instance of the dev tools to be displayed in its own window or to be
|
||||
* embedded within your UI. Only one instance per browser is available.
|
||||
*/
|
||||
public CefBrowser getDevTools();
|
||||
|
||||
/**
|
||||
* Get an instance of the dev tools to be displayed in its own window or to be
|
||||
* embedded within your UI. Only one instance per browser is available.
|
||||
*
|
||||
* @param inspectAt a position in the UI which should be inspected.
|
||||
*/
|
||||
public CefBrowser getDevTools(Point inspectAt);
|
||||
|
||||
/**
|
||||
* If a misspelled word is currently selected in an editable node calling
|
||||
* this method will replace it with the specified |word|.
|
||||
*
|
||||
* @param word replace selected word with this word.
|
||||
*/
|
||||
public void replaceMisspelling(String word);
|
||||
|
||||
/**
|
||||
* Captures a screenshot-like image of the currently displayed content and returns it.
|
||||
* <p>
|
||||
* If executed on the AWT Event Thread, this returns an immediately resolved {@link
|
||||
* java.util.concurrent.CompletableFuture}. If executed from another thread, the {@link
|
||||
* java.util.concurrent.CompletableFuture} returned is resolved as soon as the screenshot
|
||||
* has been taken (which must happen on the event thread).
|
||||
* <p>
|
||||
* The generated screenshot can either be returned as-is, containing all natively-rendered
|
||||
* pixels, or it can be scaled to match the logical width and height of the window.
|
||||
* This distinction is only relevant in case of differing logical and physical resolutions
|
||||
* (for example with HiDPI/Retina displays, which have a scaling factor of for example 2
|
||||
* between the logical width of a window (ex. 400px) and the actual number of pixels in
|
||||
* each row (ex. 800px with a scaling factor of 2)).
|
||||
*
|
||||
* @param nativeResolution whether to return an image at full native resolution (true)
|
||||
* or a scaled-down version whose width and height are equal to the logical size
|
||||
* of the screenshotted browser window
|
||||
* @return the screenshot image
|
||||
* @throws UnsupportedOperationException if not supported
|
||||
*/
|
||||
public CompletableFuture<BufferedImage> createScreenshot(boolean nativeResolution);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.CefClient;
|
||||
|
||||
/**
|
||||
* Creates a new instance of CefBrowser according the passed values
|
||||
*/
|
||||
public class CefBrowserFactory {
|
||||
public static CefBrowser create(CefClient client, String url, boolean isOffscreenRendered,
|
||||
boolean isTransparent, CefRequestContext context) {
|
||||
if (isOffscreenRendered) return new CefBrowserOsr(client, url, isTransparent, context);
|
||||
return new CefBrowserWr(client, url, context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.api.IBrowser;
|
||||
import net.montoyo.mcef.api.IStringVisitor;
|
||||
import net.montoyo.mcef.client.ClientProxy;
|
||||
import net.montoyo.mcef.client.StringVisitor;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.cef.CefClient;
|
||||
import org.cef.callback.CefDragData;
|
||||
import org.cef.handler.CefRenderHandler;
|
||||
import org.cef.handler.CefScreenInfo;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* This class represents an off-screen rendered browser.
|
||||
* The visibility of this class is "package". To create a new
|
||||
* CefBrowser instance, please use CefBrowserFactory.
|
||||
*/
|
||||
public class CefBrowserOsr extends CefBrowser_N implements CefRenderHandler, IBrowser {
|
||||
private CefRenderer renderer_;
|
||||
private long window_handle_ = 0;
|
||||
private boolean justCreated_ = false;
|
||||
private Rectangle browser_rect_ = new Rectangle(0, 0, 1, 1); // Work around CEF issue #1437.
|
||||
private Point screenPoint_ = new Point(0, 0);
|
||||
private double scaleFactor_ = 1.0;
|
||||
private int depth = 32;
|
||||
private int depth_per_component = 8;
|
||||
private boolean isTransparent_;
|
||||
private final Component dc_ = new Component() {
|
||||
@Override
|
||||
public Point getLocationOnScreen() {
|
||||
return new Point(0, 0);
|
||||
}
|
||||
};
|
||||
private MouseEvent lastMouseEvent = new MouseEvent(dc_, MouseEvent.MOUSE_MOVED, 0, 0, 0, 0, 0, false);
|
||||
public static boolean CLEANUP = true;
|
||||
|
||||
CefBrowserOsr(CefClient client, String url, boolean transparent, CefRequestContext context) {
|
||||
this(client, url, transparent, context, null, null);
|
||||
}
|
||||
|
||||
private CefBrowserOsr(CefClient client, String url, boolean transparent,
|
||||
CefRequestContext context, CefBrowserOsr parent, Point inspectAt) {
|
||||
super(client, url, context, parent, inspectAt);
|
||||
isTransparent_ = transparent;
|
||||
renderer_ = new CefRenderer(transparent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createImmediately() {
|
||||
justCreated_ = true;
|
||||
// Create the browser immediately.
|
||||
createBrowserIfRequired(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getUIComponent() {
|
||||
return dc_;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefRenderHandler getRenderHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefBrowser_N createDevToolsBrowser(CefClient client, String url,
|
||||
CefRequestContext context, CefBrowser_N parent, Point inspectAt) {
|
||||
return new CefBrowserOsr(
|
||||
client, url, isTransparent_, context, (CefBrowserOsr) this, inspectAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getViewRect(CefBrowser browser) {
|
||||
return browser_rect_;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getScreenPoint(CefBrowser browser, Point viewPoint) {
|
||||
Point screenPoint = new Point(screenPoint_);
|
||||
screenPoint.translate(viewPoint.x, viewPoint.y);
|
||||
return screenPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopupShow(CefBrowser browser, boolean show) {
|
||||
if (!show) {
|
||||
renderer_.clearPopupRects();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopupSize(CefBrowser browser, Rectangle size) {
|
||||
renderer_.onPopupSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (CLEANUP) {
|
||||
((ClientProxy) MCEF.PROXY).removeBrowser(this);
|
||||
renderer_.cleanup();
|
||||
}
|
||||
|
||||
super.close(true); //true to ignore confirmation popups
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
browser_rect_.setBounds(0, 0, width, height);
|
||||
dc_.setBounds(browser_rect_);
|
||||
dc_.setVisible(true);
|
||||
wasResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(double x1, double y1, double x2, double y2) {
|
||||
renderer_.render(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextureID() {
|
||||
return renderer_.texture_id_[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMouseMove(int x, int y, int mods, boolean left) {
|
||||
//FIXME: 'left' is not used as it causes bugs since MCEF 1.11
|
||||
|
||||
MouseEvent ev = new MouseEvent(dc_, MouseEvent.MOUSE_MOVED, 0, mods, x, y, 0, false);
|
||||
lastMouseEvent = ev;
|
||||
sendMouseEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMouseButton(int x, int y, int mods, int btn, boolean pressed, int ccnt) {
|
||||
MouseEvent ev = new MouseEvent(dc_, pressed ? MouseEvent.MOUSE_PRESSED : MouseEvent.MOUSE_RELEASED, 0, mods, x, y, ccnt, false, btn);
|
||||
sendMouseEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectKeyTyped(char c, int mods) {
|
||||
KeyEvent ev = new KeyEvent(dc_, KeyEvent.KEY_TYPED, 0, mods, 0, c);
|
||||
sendKeyEvent(ev);
|
||||
}
|
||||
|
||||
public static int remapKeycode(int kc, char c) {
|
||||
switch (kc) {
|
||||
case Keyboard.KEY_BACK:
|
||||
return KeyEvent.VK_BACK_SPACE;
|
||||
case Keyboard.KEY_DELETE:
|
||||
return KeyEvent.VK_DELETE;
|
||||
case Keyboard.KEY_DOWN:
|
||||
return KeyEvent.VK_DOWN;
|
||||
case Keyboard.KEY_RETURN:
|
||||
return KeyEvent.VK_ENTER;
|
||||
case Keyboard.KEY_ESCAPE:
|
||||
return KeyEvent.VK_ESCAPE;
|
||||
case Keyboard.KEY_LEFT:
|
||||
return KeyEvent.VK_LEFT;
|
||||
case Keyboard.KEY_RIGHT:
|
||||
return KeyEvent.VK_RIGHT;
|
||||
case Keyboard.KEY_TAB:
|
||||
return KeyEvent.VK_TAB;
|
||||
case Keyboard.KEY_UP:
|
||||
return KeyEvent.VK_UP;
|
||||
case Keyboard.KEY_PRIOR:
|
||||
return KeyEvent.VK_PAGE_UP;
|
||||
case Keyboard.KEY_NEXT:
|
||||
return KeyEvent.VK_PAGE_DOWN;
|
||||
case Keyboard.KEY_END:
|
||||
return Keyboard.KEY_END;
|
||||
case Keyboard.KEY_HOME:
|
||||
return Keyboard.KEY_HOME;
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectKeyPressedByKeyCode(int keyCode, char c, int mods) {
|
||||
if (c != '\0') {
|
||||
synchronized (WORST_HACK) {
|
||||
WORST_HACK.put(keyCode, c);
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent ev = new KeyEvent(dc_, KeyEvent.KEY_PRESSED, 0, mods, remapKeycode(keyCode, c), c);
|
||||
sendKeyEvent(ev);
|
||||
}
|
||||
|
||||
private static final Map<Integer, Character> WORST_HACK = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void injectKeyReleasedByKeyCode(int keyCode, char c, int mods) {
|
||||
if (c == '\0') {
|
||||
synchronized (WORST_HACK) {
|
||||
c = WORST_HACK.getOrDefault(keyCode, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent ev = new KeyEvent(dc_, KeyEvent.KEY_RELEASED, 0, mods, remapKeycode(keyCode, c), c);
|
||||
sendKeyEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectMouseWheel(int x, int y, int mods, int amount, int rot) {
|
||||
MouseWheelEvent ev = new MouseWheelEvent(dc_, MouseEvent.MOUSE_WHEEL, 0, mods, x, y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, amount, rot);
|
||||
sendMouseWheelEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runJS(String script, String frame) {
|
||||
executeJavaScript(script, frame, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(IStringVisitor isv) {
|
||||
getSource(new StringVisitor(isv));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPageLoading() {
|
||||
return isLoading();
|
||||
}
|
||||
|
||||
private static class PaintData {
|
||||
private ByteBuffer buffer;
|
||||
private int width;
|
||||
private int height;
|
||||
private Rectangle[] dirtyRects;
|
||||
private boolean hasFrame;
|
||||
private boolean fullReRender;
|
||||
}
|
||||
|
||||
private final PaintData paintData = new PaintData();
|
||||
|
||||
@Override
|
||||
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
|
||||
ByteBuffer buffer, int width, int height) {
|
||||
if (popup) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int size = (width * height) << 2;
|
||||
|
||||
synchronized (paintData) {
|
||||
if (buffer.limit() > size)
|
||||
Log.warning("Skipping MCEF browser frame, data is too heavy"); //TODO: Don't spam
|
||||
else {
|
||||
if (paintData.hasFrame) //The previous frame was not uploaded to GL texture, so we skip it and render this on instead
|
||||
paintData.fullReRender = true;
|
||||
|
||||
if (paintData.buffer == null || size != paintData.buffer.capacity()) //This only happens when the browser gets resized
|
||||
paintData.buffer = BufferUtils.createByteBuffer(size);
|
||||
|
||||
paintData.buffer.position(0);
|
||||
paintData.buffer.limit(buffer.limit());
|
||||
buffer.position(0);
|
||||
paintData.buffer.put(buffer);
|
||||
paintData.buffer.position(0);
|
||||
|
||||
paintData.width = width;
|
||||
paintData.height = height;
|
||||
paintData.dirtyRects = dirtyRects;
|
||||
paintData.hasFrame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mcefUpdate() {
|
||||
synchronized (paintData) {
|
||||
if (paintData.hasFrame) {
|
||||
renderer_.onPaint(false, paintData.dirtyRects, paintData.buffer, paintData.width, paintData.height, paintData.fullReRender);
|
||||
paintData.hasFrame = false;
|
||||
paintData.fullReRender = false;
|
||||
}
|
||||
}
|
||||
|
||||
//So sadly this is the only way I could get around the "youtube not rendering video if the mouse doesn't move bug"
|
||||
//Even the test browser from the original JCEF library doesn't fix this...
|
||||
//What I hope, however, is that it doesn't redraw the entire browser... otherwise I could just call "invalidate"
|
||||
sendMouseEvent(lastMouseEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCursorChange(CefBrowser browser, final int cursorType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startDragging(CefBrowser browser, CefDragData dragData, int mask, int x, int y) {
|
||||
// TODO(JCEF) Prepared for DnD support using OSR mode.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDragCursor(CefBrowser browser, int operation) {
|
||||
// TODO(JCEF) Prepared for DnD support using OSR mode.
|
||||
}
|
||||
|
||||
private void createBrowserIfRequired(boolean hasParent) {
|
||||
if (getNativeRef("CefBrowser") == 0) {
|
||||
if (getParentBrowser() != null) {
|
||||
createDevTools(getParentBrowser(), getClient(), 0, true, isTransparent_,
|
||||
null, getInspectAt());
|
||||
} else {
|
||||
createBrowser(getClient(), 0, getUrl(), true, isTransparent_, null,
|
||||
getRequestContext());
|
||||
}
|
||||
} else {
|
||||
// OSR windows cannot be reparented after creation.
|
||||
setFocus(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScreenInfo(CefBrowser browser, CefScreenInfo screenInfo) {
|
||||
screenInfo.Set(scaleFactor_, depth, depth_per_component, false, browser_rect_.getBounds(),
|
||||
browser_rect_.getBounds());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<BufferedImage> createScreenshot(boolean nativeResolution) {
|
||||
throw new NotImplementedException("createScreenshot not implemented on MCEF");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
/**
|
||||
* Interface representing system dependent methods for the browser.
|
||||
*/
|
||||
public interface CefBrowserWindow {
|
||||
/**
|
||||
* Get the window handle for the given UI object.
|
||||
*
|
||||
* @param comp a UI component
|
||||
* @return a window pointer if any
|
||||
*/
|
||||
public long getWindowHandle(Component comp);
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.CefClient;
|
||||
import org.cef.OS;
|
||||
import org.cef.handler.CefWindowHandler;
|
||||
import org.cef.handler.CefWindowHandlerAdapter;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.HierarchyBoundsListener;
|
||||
import java.awt.event.HierarchyEvent;
|
||||
import java.awt.event.HierarchyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.MenuSelectionManager;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.ToolTipManager;
|
||||
|
||||
/**
|
||||
* This class represents a windowed rendered browser.
|
||||
* The visibility of this class is "package". To create a new
|
||||
* CefBrowser instance, please use CefBrowserFactory.
|
||||
*/
|
||||
class CefBrowserWr extends CefBrowser_N {
|
||||
private Canvas canvas_ = null;
|
||||
private Component component_ = null;
|
||||
private Rectangle content_rect_ = new Rectangle(0, 0, 0, 0);
|
||||
private long window_handle_ = 0;
|
||||
private boolean justCreated_ = false;
|
||||
private double scaleFactor_ = 1.0;
|
||||
private Timer delayedUpdate_ = new Timer(100, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isClosed()) return;
|
||||
|
||||
boolean hasCreatedUI = createBrowserIfRequired(true);
|
||||
|
||||
if (hasCreatedUI) {
|
||||
delayedUpdate_.restart();
|
||||
} else {
|
||||
// If on Mac, this is needed due to the quirk described below
|
||||
// (in org.cef.browser.CefBrowserWr.CefBrowserWr(...).new JPanel()
|
||||
// {...}.paint(Graphics)). If on Linux, this is needed to invoke an
|
||||
// XMoveResizeWindow call shortly after the UI was created. That seems to be
|
||||
// necessary to actually get a windowed renderer to display something.
|
||||
if (OS.isMacintosh() || OS.isLinux()) doUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
private CefWindowHandlerAdapter win_handler_ = new CefWindowHandlerAdapter() {
|
||||
private Point lastPos = new Point(-1, -1);
|
||||
private long[] nextClick = new long[MouseInfo.getNumberOfButtons()];
|
||||
private int[] clickCnt = new int[MouseInfo.getNumberOfButtons()];
|
||||
|
||||
@Override
|
||||
public Rectangle getRect(CefBrowser browser) {
|
||||
synchronized (content_rect_) {
|
||||
return content_rect_;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseEvent(CefBrowser browser, int event, final int screenX,
|
||||
final int screenY, final int modifier, final int button) {
|
||||
final Point pt = new Point(screenX, screenY);
|
||||
if (event == MouseEvent.MOUSE_MOVED) {
|
||||
// Remove mouse-moved events if the position of the cursor hasn't
|
||||
// changed.
|
||||
if (pt.equals(lastPos)) return;
|
||||
lastPos = pt;
|
||||
|
||||
// Change mouse-moved event to mouse-dragged event if the left mouse
|
||||
// button is pressed.
|
||||
if ((modifier & MouseEvent.BUTTON1_DOWN_MASK) != 0)
|
||||
event = MouseEvent.MOUSE_DRAGGED;
|
||||
}
|
||||
|
||||
final int finalEvent = event;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Send mouse event to the root UI component instead to the browser UI.
|
||||
// Otherwise no mouse-entered and no mouse-exited events would be fired.
|
||||
Component parent = SwingUtilities.getRoot(component_);
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
SwingUtilities.convertPointFromScreen(pt, parent);
|
||||
|
||||
int clickCnt = 0;
|
||||
long now = new Date().getTime();
|
||||
if (finalEvent == MouseEvent.MOUSE_WHEEL) {
|
||||
int scrollType = MouseWheelEvent.WHEEL_UNIT_SCROLL;
|
||||
int rotation = button > 0 ? 1 : -1;
|
||||
component_.dispatchEvent(new MouseWheelEvent(parent, finalEvent, now,
|
||||
modifier, pt.x, pt.y, 0, false, scrollType, 3, rotation));
|
||||
} else {
|
||||
clickCnt = getClickCount(finalEvent, button);
|
||||
component_.dispatchEvent(new MouseEvent(parent, finalEvent, now, modifier,
|
||||
pt.x, pt.y, screenX, screenY, clickCnt, false, button));
|
||||
}
|
||||
|
||||
// Always fire a mouse-clicked event after a mouse-released event.
|
||||
if (finalEvent == MouseEvent.MOUSE_RELEASED) {
|
||||
component_.dispatchEvent(
|
||||
new MouseEvent(parent, MouseEvent.MOUSE_CLICKED, now, modifier,
|
||||
pt.x, pt.y, screenX, screenY, clickCnt, false, button));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int getClickCount(int event, int button) {
|
||||
// avoid exceptions by using modulo
|
||||
int idx = button % nextClick.length;
|
||||
|
||||
switch (event) {
|
||||
case MouseEvent.MOUSE_PRESSED:
|
||||
long currTime = new Date().getTime();
|
||||
if (currTime > nextClick[idx]) {
|
||||
nextClick[idx] = currTime
|
||||
+ (Integer) Toolkit.getDefaultToolkit().getDesktopProperty(
|
||||
"awt.multiClickInterval");
|
||||
clickCnt[idx] = 1;
|
||||
} else {
|
||||
clickCnt[idx]++;
|
||||
}
|
||||
// FALL THRU
|
||||
case MouseEvent.MOUSE_RELEASED:
|
||||
return clickCnt[idx];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CefBrowserWr(CefClient client, String url, CefRequestContext context) {
|
||||
this(client, url, context, null, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private CefBrowserWr(CefClient client, String url, CefRequestContext context,
|
||||
CefBrowserWr parent, Point inspectAt) {
|
||||
super(client, url, context, parent, inspectAt);
|
||||
delayedUpdate_.setRepeats(false);
|
||||
|
||||
// Disabling lightweight of popup menu is required because
|
||||
// otherwise it will be displayed behind the content of component_
|
||||
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
|
||||
ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
|
||||
|
||||
// We're using a JComponent instead of a Canvas now because the
|
||||
// JComponent has clipping informations, which aren't accessible for Canvas.
|
||||
component_ = new JPanel(new BorderLayout()) {
|
||||
private boolean removed_ = true;
|
||||
|
||||
@Override
|
||||
public void setBounds(int x, int y, int width, int height) {
|
||||
super.setBounds(x, y, width, height);
|
||||
wasResized((int) (width * scaleFactor_), (int) (height * scaleFactor_));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Rectangle r) {
|
||||
setBounds(r.x, r.y, r.width, r.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSize(int width, int height) {
|
||||
super.setSize(width, height);
|
||||
wasResized((int) (width * scaleFactor_), (int) (height * scaleFactor_));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSize(Dimension d) {
|
||||
setSize(d.width, d.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
// If the user resizes the UI component, the new size and clipping
|
||||
// informations are forwarded to the native code.
|
||||
// But on Mac the last resize information doesn't resize the native UI
|
||||
// accurately (sometimes the native UI is too small). An easy way to
|
||||
// solve this, is to send the last Update-Information again. Therefore
|
||||
// we're setting up a delayedUpdate timer which is reset each time
|
||||
// paint is called. This prevents the us of sending the UI update too
|
||||
// often.
|
||||
if (g instanceof Graphics2D) {
|
||||
scaleFactor_ = ((Graphics2D) g).getTransform().getScaleX();
|
||||
}
|
||||
doUpdate();
|
||||
delayedUpdate_.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
if (removed_) {
|
||||
setParent(getWindowHandle(this), canvas_);
|
||||
removed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
if (!removed_) {
|
||||
if (!isClosed()) {
|
||||
setParent(0, null);
|
||||
}
|
||||
removed_ = true;
|
||||
}
|
||||
super.removeNotify();
|
||||
}
|
||||
};
|
||||
|
||||
// On windows we have to use a Canvas because its a heavyweight component
|
||||
// and we need its native HWND as parent for the browser UI. The same
|
||||
// technique is used on Linux as well.
|
||||
if (OS.isWindows() || OS.isLinux()) {
|
||||
canvas_ = new Canvas();
|
||||
((JPanel) component_).add(canvas_, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
// Initial minimal size of the component. Otherwise the UI won't work
|
||||
// accordingly in panes like JSplitPane.
|
||||
component_.setMinimumSize(new Dimension(0, 0));
|
||||
component_.setFocusable(true);
|
||||
component_.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
setFocus(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
// Dismiss any Java menus that are currently displayed.
|
||||
MenuSelectionManager.defaultManager().clearSelectedPath();
|
||||
setFocus(true);
|
||||
}
|
||||
});
|
||||
component_.addHierarchyBoundsListener(new HierarchyBoundsListener() {
|
||||
@Override
|
||||
public void ancestorResized(HierarchyEvent e) {
|
||||
doUpdate();
|
||||
}
|
||||
@Override
|
||||
public void ancestorMoved(HierarchyEvent e) {
|
||||
doUpdate();
|
||||
notifyMoveOrResizeStarted();
|
||||
}
|
||||
});
|
||||
component_.addHierarchyListener(new HierarchyListener() {
|
||||
@Override
|
||||
public void hierarchyChanged(HierarchyEvent e) {
|
||||
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
|
||||
setWindowVisibility(e.getChanged().isVisible());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createImmediately() {
|
||||
justCreated_ = true;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Create the browser immediately. It will be parented to the Java
|
||||
// window once it becomes available.
|
||||
createBrowserIfRequired(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getUIComponent() {
|
||||
return component_;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefWindowHandler getWindowHandler() {
|
||||
return win_handler_;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CefBrowser_N createDevToolsBrowser(CefClient client, String url,
|
||||
CefRequestContext context, CefBrowser_N parent, Point inspectAt) {
|
||||
return new CefBrowserWr(client, url, context, (CefBrowserWr) this, inspectAt);
|
||||
}
|
||||
|
||||
private synchronized long getWindowHandle() {
|
||||
if (window_handle_ == 0 && OS.isMacintosh()) {
|
||||
window_handle_ = getWindowHandle(component_);
|
||||
}
|
||||
return window_handle_;
|
||||
}
|
||||
|
||||
private static long getWindowHandle(Component component) {
|
||||
if (OS.isMacintosh()) {
|
||||
try {
|
||||
Class<?> cls = Class.forName("org.cef.browser.mac.CefBrowserWindowMac");
|
||||
CefBrowserWindow browserWindow = (CefBrowserWindow) cls.newInstance();
|
||||
if (browserWindow != null) {
|
||||
return browserWindow.getWindowHandle(component);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void doUpdate() {
|
||||
if (isClosed()) return;
|
||||
|
||||
Rectangle vr = ((JPanel) component_).getVisibleRect();
|
||||
Rectangle clipping = new Rectangle((int) (vr.getX() * scaleFactor_),
|
||||
(int) (vr.getY() * scaleFactor_), (int) (vr.getWidth() * scaleFactor_),
|
||||
(int) (vr.getHeight() * scaleFactor_));
|
||||
|
||||
if (OS.isMacintosh()) {
|
||||
Container parent = component_.getParent();
|
||||
Point contentPos = component_.getLocation();
|
||||
while (parent != null) {
|
||||
Container next = parent.getParent();
|
||||
if (next != null && next instanceof Window) break;
|
||||
Point parentPos = parent.getLocation();
|
||||
contentPos.translate(parentPos.x, parentPos.y);
|
||||
parent = next;
|
||||
}
|
||||
contentPos.translate(clipping.x, clipping.y);
|
||||
|
||||
Point browserPos = clipping.getLocation();
|
||||
browserPos.x *= -1;
|
||||
browserPos.y *= -1;
|
||||
|
||||
synchronized (content_rect_) {
|
||||
content_rect_ = new Rectangle(contentPos, clipping.getSize());
|
||||
Rectangle browserRect = new Rectangle(browserPos, component_.getSize());
|
||||
updateUI(content_rect_, browserRect);
|
||||
}
|
||||
} else {
|
||||
synchronized (content_rect_) {
|
||||
Rectangle bounds = component_.getBounds();
|
||||
content_rect_ = new Rectangle((int) (bounds.getX() * scaleFactor_),
|
||||
(int) (bounds.getY() * scaleFactor_),
|
||||
(int) (bounds.getWidth() * scaleFactor_),
|
||||
(int) (bounds.getHeight() * scaleFactor_));
|
||||
updateUI(clipping, content_rect_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createBrowserIfRequired(boolean hasParent) {
|
||||
if (isClosed()) return false;
|
||||
|
||||
long windowHandle = 0;
|
||||
Component canvas = null;
|
||||
if (hasParent) {
|
||||
windowHandle = getWindowHandle();
|
||||
canvas = (OS.isWindows() || OS.isLinux()) ? canvas_ : component_;
|
||||
}
|
||||
|
||||
if (getNativeRef("CefBrowser") == 0) {
|
||||
if (getParentBrowser() != null) {
|
||||
createDevTools(getParentBrowser(), getClient(), windowHandle, false, false, canvas,
|
||||
getInspectAt());
|
||||
return true;
|
||||
} else {
|
||||
createBrowser(getClient(), windowHandle, getUrl(), false, false, canvas,
|
||||
getRequestContext());
|
||||
return true;
|
||||
}
|
||||
} else if (hasParent && justCreated_) {
|
||||
setParent(windowHandle, canvas);
|
||||
setFocus(true);
|
||||
justCreated_ = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<BufferedImage> createScreenshot(boolean nativeResolution) {
|
||||
throw new UnsupportedOperationException("Unsupported for windowed rendering");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,817 @@
|
||||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.CefClient;
|
||||
import org.cef.browser.CefRequestContext;
|
||||
import org.cef.callback.CefDragData;
|
||||
import org.cef.callback.CefNativeAdapter;
|
||||
import org.cef.callback.CefPdfPrintCallback;
|
||||
import org.cef.callback.CefRunFileDialogCallback;
|
||||
import org.cef.callback.CefStringVisitor;
|
||||
import org.cef.handler.CefClientHandler;
|
||||
import org.cef.handler.CefDialogHandler.FileDialogMode;
|
||||
import org.cef.handler.CefRenderHandler;
|
||||
import org.cef.handler.CefWindowHandler;
|
||||
import org.cef.misc.CefPdfPrintSettings;
|
||||
import org.cef.network.CefRequest;
|
||||
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Component;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* This class represents all methods which are connected to the
|
||||
* native counterpart CEF.
|
||||
* The visibility of this class is "package". To create a new
|
||||
* CefBrowser instance, please use CefBrowserFactory.
|
||||
*/
|
||||
abstract class CefBrowser_N extends CefNativeAdapter implements CefBrowser {
|
||||
private volatile boolean isPending_ = false;
|
||||
private final CefClient client_;
|
||||
private final String url_;
|
||||
private final CefRequestContext request_context_;
|
||||
private volatile CefBrowser_N parent_ = null;
|
||||
private volatile Point inspectAt_ = null;
|
||||
private volatile CefBrowser_N devTools_ = null;
|
||||
private boolean closeAllowed_ = false;
|
||||
private volatile boolean isClosed_ = false;
|
||||
private volatile boolean isClosing_ = false;
|
||||
|
||||
protected CefBrowser_N(CefClient client, String url, CefRequestContext context,
|
||||
CefBrowser_N parent, Point inspectAt) {
|
||||
client_ = client;
|
||||
url_ = url;
|
||||
request_context_ = context;
|
||||
parent_ = parent;
|
||||
inspectAt_ = inspectAt;
|
||||
}
|
||||
|
||||
protected String getUrl() {
|
||||
return url_;
|
||||
}
|
||||
|
||||
protected CefRequestContext getRequestContext() {
|
||||
return request_context_;
|
||||
}
|
||||
|
||||
protected CefBrowser_N getParentBrowser() {
|
||||
return parent_;
|
||||
}
|
||||
|
||||
protected Point getInspectAt() {
|
||||
return inspectAt_;
|
||||
}
|
||||
|
||||
protected boolean isClosed() {
|
||||
return isClosed_;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefClient getClient() {
|
||||
return client_;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefRenderHandler getRenderHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefWindowHandler getWindowHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setCloseAllowed() {
|
||||
closeAllowed_ = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean doClose() {
|
||||
if (closeAllowed_) {
|
||||
// Allow the close to proceed.
|
||||
return false;
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Trigger close of the parent window.
|
||||
Component parent = SwingUtilities.getRoot(getUIComponent());
|
||||
if (parent != null) {
|
||||
parent.dispatchEvent(
|
||||
new WindowEvent((Window) parent, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Cancel the close.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onBeforeClose() {
|
||||
isClosed_ = true;
|
||||
if (request_context_ != null) request_context_.dispose();
|
||||
if (parent_ != null) {
|
||||
parent_.closeDevTools();
|
||||
parent_.devTools_ = null;
|
||||
parent_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefBrowser getDevTools() {
|
||||
return getDevTools(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized CefBrowser getDevTools(Point inspectAt) {
|
||||
if (devTools_ == null) {
|
||||
devTools_ = createDevToolsBrowser(client_, url_, request_context_, this, inspectAt);
|
||||
}
|
||||
return devTools_;
|
||||
}
|
||||
|
||||
protected abstract CefBrowser_N createDevToolsBrowser(CefClient client, String url,
|
||||
CefRequestContext context, CefBrowser_N parent, Point inspectAt);
|
||||
|
||||
/**
|
||||
* Create a new browser.
|
||||
*/
|
||||
protected void createBrowser(CefClientHandler clientHandler, long windowHandle, String url,
|
||||
boolean osr, boolean transparent, Component canvas, CefRequestContext context) {
|
||||
if (getNativeRef("CefBrowser") == 0 && !isPending_) {
|
||||
try {
|
||||
N_CreateBrowser(
|
||||
clientHandler, windowHandle, url, osr, transparent, canvas, context);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called async from the (native) main UI thread.
|
||||
*/
|
||||
private void notifyBrowserCreated() {
|
||||
isPending_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new browser as dev tools
|
||||
*/
|
||||
protected final void createDevTools(CefBrowser_N parent, CefClientHandler clientHandler,
|
||||
long windowHandle, boolean osr, boolean transparent, Component canvas,
|
||||
Point inspectAt) {
|
||||
if (getNativeRef("CefBrowser") == 0 && !isPending_) {
|
||||
try {
|
||||
isPending_ = N_CreateDevTools(
|
||||
parent, clientHandler, windowHandle, osr, transparent, canvas, inspectAt);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the native window handle for the specified native surface handle.
|
||||
*/
|
||||
protected final long getWindowHandle(long surfaceHandle) {
|
||||
try {
|
||||
return N_GetWindowHandle(surfaceHandle);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close(true);
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGoBack() {
|
||||
try {
|
||||
return N_CanGoBack();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goBack() {
|
||||
try {
|
||||
N_GoBack();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGoForward() {
|
||||
try {
|
||||
return N_CanGoForward();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goForward() {
|
||||
try {
|
||||
N_GoForward();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
try {
|
||||
return N_IsLoading();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
try {
|
||||
N_Reload();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadIgnoreCache() {
|
||||
try {
|
||||
N_ReloadIgnoreCache();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLoad() {
|
||||
try {
|
||||
N_StopLoad();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIdentifier() {
|
||||
try {
|
||||
return N_GetIdentifier();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefFrame getMainFrame() {
|
||||
try {
|
||||
return N_GetMainFrame();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefFrame getFocusedFrame() {
|
||||
try {
|
||||
return N_GetFocusedFrame();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefFrame getFrame(long identifier) {
|
||||
try {
|
||||
return N_GetFrame(identifier);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefFrame getFrame(String name) {
|
||||
try {
|
||||
return N_GetFrame2(name);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<Long> getFrameIdentifiers() {
|
||||
try {
|
||||
return N_GetFrameIdentifiers();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<String> getFrameNames() {
|
||||
try {
|
||||
return N_GetFrameNames();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrameCount() {
|
||||
try {
|
||||
return N_GetFrameCount();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPopup() {
|
||||
try {
|
||||
return N_IsPopup();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDocument() {
|
||||
try {
|
||||
return N_HasDocument();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void viewSource() {
|
||||
try {
|
||||
N_ViewSource();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void getSource(CefStringVisitor visitor) {
|
||||
try {
|
||||
N_GetSource(visitor);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void getText(CefStringVisitor visitor) {
|
||||
try {
|
||||
N_GetText(visitor);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRequest(CefRequest request) {
|
||||
try {
|
||||
N_LoadRequest(request);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadURL(String url) {
|
||||
try {
|
||||
N_LoadURL(url);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeJavaScript(String code, String url, int line) {
|
||||
try {
|
||||
N_ExecuteJavaScript(code, url, line);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURL() {
|
||||
try {
|
||||
return N_GetURL();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean force) {
|
||||
if (isClosing_ || isClosed_) return;
|
||||
if (force) isClosing_ = true;
|
||||
|
||||
try {
|
||||
N_Close(force);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocus(boolean enable) {
|
||||
try {
|
||||
N_SetFocus(enable);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowVisibility(boolean visible) {
|
||||
try {
|
||||
N_SetWindowVisibility(visible);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZoomLevel() {
|
||||
try {
|
||||
return N_GetZoomLevel();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZoomLevel(double zoomLevel) {
|
||||
try {
|
||||
N_SetZoomLevel(zoomLevel);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runFileDialog(FileDialogMode mode, String title, String defaultFilePath,
|
||||
Vector<String> acceptFilters, int selectedAcceptFilter,
|
||||
CefRunFileDialogCallback callback) {
|
||||
try {
|
||||
N_RunFileDialog(
|
||||
mode, title, defaultFilePath, acceptFilters, selectedAcceptFilter, callback);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDownload(String url) {
|
||||
try {
|
||||
N_StartDownload(url);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print() {
|
||||
try {
|
||||
N_Print();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printToPDF(
|
||||
String path, CefPdfPrintSettings settings, CefPdfPrintCallback callback) {
|
||||
if (path == null || path.isEmpty()) {
|
||||
throw new IllegalArgumentException("path was null or empty");
|
||||
}
|
||||
try {
|
||||
N_PrintToPDF(path, settings, callback);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void find(String searchText, boolean forward, boolean matchCase, boolean findNext) {
|
||||
try {
|
||||
N_Find(searchText, forward, matchCase, findNext);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopFinding(boolean clearSelection) {
|
||||
try {
|
||||
N_StopFinding(clearSelection);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void closeDevTools() {
|
||||
try {
|
||||
N_CloseDevTools();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceMisspelling(String word) {
|
||||
try {
|
||||
N_ReplaceMisspelling(word);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that the browser was resized.
|
||||
* @param width The new width of the browser
|
||||
* @param height The new height of the browser
|
||||
*/
|
||||
protected final void wasResized(int width, int height) {
|
||||
try {
|
||||
N_WasResized(width, height);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the UI.
|
||||
*/
|
||||
protected final void invalidate() {
|
||||
try {
|
||||
N_Invalidate();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a key event.
|
||||
* @param e The event to send.
|
||||
*/
|
||||
protected final void sendKeyEvent(KeyEvent e) {
|
||||
try {
|
||||
N_SendKeyEvent(e);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a mouse event.
|
||||
* @param e The event to send.
|
||||
*/
|
||||
protected final void sendMouseEvent(MouseEvent e) {
|
||||
try {
|
||||
N_SendMouseEvent(e);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a mouse wheel event.
|
||||
* @param e The event to send.
|
||||
*/
|
||||
protected final void sendMouseWheelEvent(MouseWheelEvent e) {
|
||||
try {
|
||||
N_SendMouseWheelEvent(e);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when the user drags the mouse into the web view (before
|
||||
* calling DragTargetDragOver/DragTargetLeave/DragTargetDrop).
|
||||
* |drag_data| should not contain file contents as this type of data is not
|
||||
* allowed to be dragged into the web view. File contents can be removed using
|
||||
* CefDragData::ResetFileContents (for example, if |drag_data| comes from
|
||||
* CefRenderHandler::StartDragging).
|
||||
* This method is only used when window rendering is disabled.
|
||||
*/
|
||||
protected final void dragTargetDragEnter(
|
||||
CefDragData dragData, Point pos, int modifiers, int allowedOps) {
|
||||
try {
|
||||
N_DragTargetDragEnter(dragData, pos, modifiers, allowedOps);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method each time the mouse is moved across the web view during
|
||||
* a drag operation (after calling DragTargetDragEnter and before calling
|
||||
* DragTargetDragLeave/DragTargetDrop).
|
||||
* This method is only used when window rendering is disabled.
|
||||
*/
|
||||
protected final void dragTargetDragOver(Point pos, int modifiers, int allowedOps) {
|
||||
try {
|
||||
N_DragTargetDragOver(pos, modifiers, allowedOps);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when the user drags the mouse out of the web view (after
|
||||
* calling DragTargetDragEnter).
|
||||
* This method is only used when window rendering is disabled.
|
||||
*/
|
||||
protected final void dragTargetDragLeave() {
|
||||
try {
|
||||
N_DragTargetDragLeave();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when the user completes the drag operation by dropping
|
||||
* the object onto the web view (after calling DragTargetDragEnter).
|
||||
* The object being dropped is |drag_data|, given as an argument to
|
||||
* the previous DragTargetDragEnter call.
|
||||
* This method is only used when window rendering is disabled.
|
||||
*/
|
||||
protected final void dragTargetDrop(Point pos, int modifiers) {
|
||||
try {
|
||||
N_DragTargetDrop(pos, modifiers);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when the drag operation started by a
|
||||
* CefRenderHandler.startDragging call has ended either in a drop or
|
||||
* by being cancelled. |x| and |y| are mouse coordinates relative to the
|
||||
* upper-left corner of the view. If the web view is both the drag source
|
||||
* and the drag target then all DragTarget* methods should be called before
|
||||
* DragSource* methods.
|
||||
* This method is only used when window rendering is disabled.
|
||||
*/
|
||||
protected final void dragSourceEndedAt(Point pos, int operation) {
|
||||
try {
|
||||
N_DragSourceEndedAt(pos, operation);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method when the drag operation started by a
|
||||
* CefRenderHandler.startDragging call has completed. This method may be
|
||||
* called immediately without first calling DragSourceEndedAt to cancel a
|
||||
* drag operation. If the web view is both the drag source and the drag
|
||||
* target then all DragTarget* methods should be called before DragSource*
|
||||
* methods.
|
||||
* This method is only used when window rendering is disabled.
|
||||
*/
|
||||
protected final void dragSourceSystemDragEnded() {
|
||||
try {
|
||||
N_DragSourceSystemDragEnded();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void updateUI(Rectangle contentRect, Rectangle browserRect) {
|
||||
try {
|
||||
N_UpdateUI(contentRect, browserRect);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void setParent(long windowHandle, Component canvas) {
|
||||
if (isClosing_ || isClosed_) return;
|
||||
|
||||
try {
|
||||
N_SetParent(windowHandle, canvas);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method if the browser frame was moved.
|
||||
* This fixes positioning of select popups and dismissal on window move/resize.
|
||||
*/
|
||||
protected final void notifyMoveOrResizeStarted() {
|
||||
try {
|
||||
N_NotifyMoveOrResizeStarted();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native boolean N_CreateBrowser(CefClientHandler clientHandler, long windowHandle,
|
||||
String url, boolean osr, boolean transparent, Component canvas,
|
||||
CefRequestContext context);
|
||||
private final native boolean N_CreateDevTools(CefBrowser parent, CefClientHandler clientHandler,
|
||||
long windowHandle, boolean osr, boolean transparent, Component canvas, Point inspectAt);
|
||||
private final native long N_GetWindowHandle(long surfaceHandle);
|
||||
private final native boolean N_CanGoBack();
|
||||
private final native void N_GoBack();
|
||||
private final native boolean N_CanGoForward();
|
||||
private final native void N_GoForward();
|
||||
private final native boolean N_IsLoading();
|
||||
private final native void N_Reload();
|
||||
private final native void N_ReloadIgnoreCache();
|
||||
private final native void N_StopLoad();
|
||||
private final native int N_GetIdentifier();
|
||||
private final native CefFrame N_GetMainFrame();
|
||||
private final native CefFrame N_GetFocusedFrame();
|
||||
private final native CefFrame N_GetFrame(long identifier);
|
||||
private final native CefFrame N_GetFrame2(String name);
|
||||
private final native Vector<Long> N_GetFrameIdentifiers();
|
||||
private final native Vector<String> N_GetFrameNames();
|
||||
private final native int N_GetFrameCount();
|
||||
private final native boolean N_IsPopup();
|
||||
private final native boolean N_HasDocument();
|
||||
private final native void N_ViewSource();
|
||||
private final native void N_GetSource(CefStringVisitor visitor);
|
||||
private final native void N_GetText(CefStringVisitor visitor);
|
||||
private final native void N_LoadRequest(CefRequest request);
|
||||
private final native void N_LoadURL(String url);
|
||||
private final native void N_ExecuteJavaScript(String code, String url, int line);
|
||||
private final native String N_GetURL();
|
||||
private final native void N_Close(boolean force);
|
||||
private final native void N_SetFocus(boolean enable);
|
||||
private final native void N_SetWindowVisibility(boolean visible);
|
||||
private final native double N_GetZoomLevel();
|
||||
private final native void N_SetZoomLevel(double zoomLevel);
|
||||
private final native void N_RunFileDialog(FileDialogMode mode, String title,
|
||||
String defaultFilePath, Vector<String> acceptFilters, int selectedAcceptFilter,
|
||||
CefRunFileDialogCallback callback);
|
||||
private final native void N_StartDownload(String url);
|
||||
private final native void N_Print();
|
||||
private final native void N_PrintToPDF(
|
||||
String path, CefPdfPrintSettings settings, CefPdfPrintCallback callback);
|
||||
private final native void N_Find(
|
||||
String searchText, boolean forward, boolean matchCase, boolean findNext);
|
||||
private final native void N_StopFinding(boolean clearSelection);
|
||||
private final native void N_CloseDevTools();
|
||||
private final native void N_ReplaceMisspelling(String word);
|
||||
private final native void N_WasResized(int width, int height);
|
||||
private final native void N_Invalidate();
|
||||
private final native void N_SendKeyEvent(KeyEvent e);
|
||||
private final native void N_SendMouseEvent(MouseEvent e);
|
||||
private final native void N_SendMouseWheelEvent(MouseWheelEvent e);
|
||||
private final native void N_DragTargetDragEnter(
|
||||
CefDragData dragData, Point pos, int modifiers, int allowed_ops);
|
||||
private final native void N_DragTargetDragOver(Point pos, int modifiers, int allowed_ops);
|
||||
private final native void N_DragTargetDragLeave();
|
||||
private final native void N_DragTargetDrop(Point pos, int modifiers);
|
||||
private final native void N_DragSourceEndedAt(Point pos, int operation);
|
||||
private final native void N_DragSourceSystemDragEnded();
|
||||
private final native void N_UpdateUI(Rectangle contentRect, Rectangle browserRect);
|
||||
private final native void N_SetParent(long windowHandle, Component canvas);
|
||||
private final native void N_NotifyMoveOrResizeStarted();
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.callback.CefDragData;
|
||||
import org.cef.misc.EventFlags;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DropTarget;
|
||||
import java.awt.dnd.DropTargetDragEvent;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.awt.dnd.DropTargetEvent;
|
||||
import java.awt.dnd.DropTargetListener;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
class CefDropTargetListener implements DropTargetListener {
|
||||
private CefBrowser_N browser_;
|
||||
private CefDragData dragData_ = null;
|
||||
private int dragOperations_ = CefDragData.DragOperations.DRAG_OPERATION_COPY;
|
||||
private int dragModifiers_ = EventFlags.EVENTFLAG_NONE;
|
||||
private int acceptOperations_ = DnDConstants.ACTION_COPY;
|
||||
|
||||
CefDropTargetListener(CefBrowser_N browser) {
|
||||
browser_ = browser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragEnter(DropTargetDragEvent event) {
|
||||
CreateDragData(event);
|
||||
browser_.dragTargetDragEnter(
|
||||
dragData_, event.getLocation(), dragModifiers_, dragOperations_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragExit(DropTargetEvent event) {
|
||||
AssertDragData();
|
||||
browser_.dragTargetDragLeave();
|
||||
ClearDragData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragOver(DropTargetDragEvent event) {
|
||||
AssertDragData();
|
||||
browser_.dragTargetDragOver(event.getLocation(), dragModifiers_, dragOperations_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(DropTargetDragEvent event) {
|
||||
AssertDragData();
|
||||
acceptOperations_ = event.getDropAction();
|
||||
switch (acceptOperations_) {
|
||||
case DnDConstants.ACTION_LINK:
|
||||
dragOperations_ = CefDragData.DragOperations.DRAG_OPERATION_LINK;
|
||||
dragModifiers_ =
|
||||
EventFlags.EVENTFLAG_CONTROL_DOWN | EventFlags.EVENTFLAG_SHIFT_DOWN;
|
||||
break;
|
||||
case DnDConstants.ACTION_COPY:
|
||||
dragOperations_ = CefDragData.DragOperations.DRAG_OPERATION_COPY;
|
||||
dragModifiers_ = EventFlags.EVENTFLAG_CONTROL_DOWN;
|
||||
break;
|
||||
case DnDConstants.ACTION_MOVE:
|
||||
dragOperations_ = CefDragData.DragOperations.DRAG_OPERATION_MOVE;
|
||||
dragModifiers_ = EventFlags.EVENTFLAG_SHIFT_DOWN;
|
||||
break;
|
||||
case DnDConstants.ACTION_NONE:
|
||||
// The user did not select an action, so use COPY as the default.
|
||||
dragOperations_ = CefDragData.DragOperations.DRAG_OPERATION_COPY;
|
||||
dragModifiers_ = EventFlags.EVENTFLAG_NONE;
|
||||
acceptOperations_ = DnDConstants.ACTION_COPY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(DropTargetDropEvent event) {
|
||||
AssertDragData();
|
||||
browser_.dragTargetDrop(event.getLocation(), dragModifiers_);
|
||||
event.acceptDrop(acceptOperations_);
|
||||
event.dropComplete(true);
|
||||
ClearDragData();
|
||||
}
|
||||
|
||||
private void CreateDragData(DropTargetDragEvent event) {
|
||||
assert dragData_ == null;
|
||||
dragData_ = createDragData(event);
|
||||
dropActionChanged(event);
|
||||
}
|
||||
|
||||
private void AssertDragData() {
|
||||
assert dragData_ != null;
|
||||
}
|
||||
|
||||
private void ClearDragData() {
|
||||
dragData_ = null;
|
||||
}
|
||||
|
||||
private static CefDragData createDragData(DropTargetDragEvent event) {
|
||||
CefDragData dragData = CefDragData.create();
|
||||
|
||||
Transferable transferable = event.getTransferable();
|
||||
DataFlavor[] flavors = transferable.getTransferDataFlavors();
|
||||
for (DataFlavor flavor : flavors) {
|
||||
try {
|
||||
if (flavor.isFlavorTextType()) {
|
||||
Object ob = transferable.getTransferData(flavor);
|
||||
if (!(ob instanceof String)) continue;
|
||||
dragData.setFragmentText((String) ob);
|
||||
} else if (flavor.isFlavorJavaFileListType()) {
|
||||
List<File> files = (List<File>) transferable.getTransferData(flavor);
|
||||
for (File file : files) {
|
||||
dragData.addFile(file.getPath(), file.getName());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Data is no longer available or of unsupported flavor.
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return dragData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
/**
|
||||
* Interface representing a frame.
|
||||
*/
|
||||
public interface CefFrame {
|
||||
/**
|
||||
* Removes the native reference from an unused object.
|
||||
*/
|
||||
void dispose();
|
||||
|
||||
/**
|
||||
* Returns the globally unique identifier for this frame or < 0 if the
|
||||
* underlying frame does not yet exist.
|
||||
* @return The frame identifier
|
||||
*/
|
||||
long getIdentifier();
|
||||
|
||||
/**
|
||||
* Emits the URL currently loaded in this frame.
|
||||
* @return the URL currently loaded in this frame.
|
||||
*/
|
||||
String getURL();
|
||||
|
||||
/**
|
||||
* Returns the name for this frame. If the frame has an assigned name (for
|
||||
* example, set via the iframe "name" attribute) then that value will be
|
||||
* returned. Otherwise a unique name will be constructed based on the frame
|
||||
* parent hierarchy. The main (top-level) frame will always have an empty name
|
||||
* value.
|
||||
* @return The frame name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns true if this is the main (top-level) frame.
|
||||
* @return True if this frame is top-level otherwise false.
|
||||
*/
|
||||
boolean isMain();
|
||||
|
||||
/**
|
||||
* True if this object is currently attached to a valid frame.
|
||||
* @return True if valid otherwise false.
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
/**
|
||||
* Returns true if this is the focused frame.
|
||||
* @return True if valid otherwise false.
|
||||
*/
|
||||
boolean isFocused();
|
||||
|
||||
/**
|
||||
* Returns the parent of this frame or NULL if this is the main (top-level)
|
||||
* frame.
|
||||
* @return The parent frame or NULL if this is the main frame
|
||||
*/
|
||||
CefFrame getParent();
|
||||
|
||||
/**
|
||||
* Execute a string of JavaScript code in this frame. The url
|
||||
* parameter is the URL where the script in question can be found, if any.
|
||||
* The renderer may request this URL to show the developer the source of the
|
||||
* error. The line parameter is the base line number to use for error
|
||||
* reporting.
|
||||
*
|
||||
* @param code The code to be executed.
|
||||
* @param url The URL where the script in question can be found.
|
||||
* @param line The base line number to use for error reporting.
|
||||
*/
|
||||
public void executeJavaScript(String code, String url, int line);
|
||||
|
||||
/**
|
||||
* Execute undo in this frame.
|
||||
*/
|
||||
public void undo();
|
||||
|
||||
/**
|
||||
* Execute redo in this frame.
|
||||
*/
|
||||
public void redo();
|
||||
|
||||
/**
|
||||
* Execute cut in this frame.
|
||||
*/
|
||||
public void cut();
|
||||
|
||||
/**
|
||||
* Execute copy in this frame.
|
||||
*/
|
||||
public void copy();
|
||||
|
||||
/**
|
||||
* Execute paste in this frame.
|
||||
*/
|
||||
public void paste();
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.callback.CefNativeAdapter;
|
||||
|
||||
/**
|
||||
* This class represents all methods which are connected to the
|
||||
* native counterpart CEF.
|
||||
* The visibility of this class is "package".
|
||||
*/
|
||||
class CefFrame_N extends CefNativeAdapter implements CefFrame {
|
||||
CefFrame_N() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
dispose();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
N_Dispose(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdentifier() {
|
||||
try {
|
||||
return N_GetIdentifier(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURL() {
|
||||
try {
|
||||
return N_GetURL(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
try {
|
||||
return N_GetName(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMain() {
|
||||
try {
|
||||
return N_IsMain(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
try {
|
||||
return N_IsValid(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
try {
|
||||
return N_IsFocused(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefFrame getParent() {
|
||||
try {
|
||||
return N_GetParent(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeJavaScript(String code, String url, int line) {
|
||||
try {
|
||||
N_ExecuteJavaScript(getNativeRef(null), code, url, line);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
try {
|
||||
N_Undo(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
try {
|
||||
N_Redo(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void cut() {
|
||||
try {
|
||||
N_Cut(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void copy() {
|
||||
try {
|
||||
N_Copy(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void paste() {
|
||||
try {
|
||||
N_Paste(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void N_Dispose(long self);
|
||||
private final native long N_GetIdentifier(long self);
|
||||
private final native String N_GetURL(long self);
|
||||
private final native String N_GetName(long self);
|
||||
private final native boolean N_IsMain(long self);
|
||||
private final native boolean N_IsValid(long self);
|
||||
private final native boolean N_IsFocused(long self);
|
||||
private final native CefFrame N_GetParent(long self);
|
||||
private final native void N_ExecuteJavaScript(long self, String code, String url, int line);
|
||||
private final native void N_Undo(long self);
|
||||
private final native void N_Redo(long self);
|
||||
private final native void N_Cut(long self);
|
||||
private final native void N_Copy(long self);
|
||||
private final native void N_Paste(long self);
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.handler.CefMessageRouterHandler;
|
||||
|
||||
/**
|
||||
* The below classes implement support for routing aynchronous messages between
|
||||
* JavaScript running in the renderer process and C++ running in the browser
|
||||
* process. An application interacts with the router by passing it data from
|
||||
* standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageRecieved,
|
||||
* OnContextCreated, etc). The renderer-side router supports generic JavaScript
|
||||
* callback registration and execution while the browser-side router supports
|
||||
* application-specific logic via one or more application-provided Handler
|
||||
* instances.
|
||||
*
|
||||
* The renderer-side router implementation exposes a query function and a cancel
|
||||
* function via the JavaScript 'window' object:
|
||||
*
|
||||
* // Create and send a new query.
|
||||
* var request_id = window.cefQuery({
|
||||
* request: 'my_request',
|
||||
* persistent: false,
|
||||
* onSuccess: function(response) {},
|
||||
* onFailure: function(error_code, error_message) {}
|
||||
* });
|
||||
*
|
||||
* // Optionally cancel the query.
|
||||
* window.cefQueryCancel(request_id);
|
||||
*
|
||||
* When |window.cefQuery| is executed the request is sent asynchronously to one
|
||||
* or more C++ Handler objects registered in the browser process. Each C++
|
||||
* Handler can choose to either handle or ignore the query in the
|
||||
* Handler::OnQuery callback. If a Handler chooses to handle the query then it
|
||||
* should execute Callback::Success when a response is available or
|
||||
* Callback::Failure if an error occurs. This will result in asynchronous
|
||||
* execution of the associated JavaScript callback in the renderer process. Any
|
||||
* queries unhandled by C++ code in the browser process will be automatically
|
||||
* canceled and the associated JavaScript onFailure callback will be executed
|
||||
* with an error code of -1.
|
||||
*
|
||||
* Queries can be either persistent or non-persistent. If the query is
|
||||
* persistent than the callbacks will remain registered until one of the
|
||||
* following conditions are met:
|
||||
*
|
||||
* A. The query is canceled in JavaScript using the |window.cefQueryCancel|
|
||||
* function.
|
||||
* B. The query is canceled in C++ code using the Callback::Failure function.
|
||||
* C. The context associated with the query is released due to browser
|
||||
* destruction, navigation or renderer process termination.
|
||||
*
|
||||
* If the query is non-persistent then the registration will be removed after
|
||||
* the JavaScript callback is executed a single time. If a query is canceled for
|
||||
* a reason other than Callback::Failure being executed then the associated
|
||||
* Handler's OnQueryCanceled method will be called.
|
||||
*
|
||||
* Some possible usage patterns include:
|
||||
*
|
||||
* One-time Request. Use a non-persistent query to send a JavaScript request.
|
||||
* The Handler evaluates the request and returns the response. The query is
|
||||
* then discarded.
|
||||
*
|
||||
* Broadcast. Use a persistent query to register as a JavaScript broadcast
|
||||
* receiver. The Handler keeps track of all registered Callbacks and executes
|
||||
* them sequentially to deliver the broadcast message.
|
||||
*
|
||||
* Subscription. Use a persistent query to register as a JavaScript subscription
|
||||
* receiver. The Handler initiates the subscription feed on the first request
|
||||
* and delivers responses to all registered subscribers as they become
|
||||
* available. The Handler cancels the subscription feed when there are no
|
||||
* longer any registered JavaScript receivers.
|
||||
*
|
||||
* Message routing occurs on a per-browser and per-context basis. Consequently,
|
||||
* additional application logic can be applied by restricting which browser or
|
||||
* context instances are passed into the router. If you choose to use this
|
||||
* approach do so cautiously. In order for the router to function correctly any
|
||||
* browser or context instance passed into a single router callback must then
|
||||
* be passed into all router callbacks.
|
||||
*
|
||||
* There is generally no need to have multiple renderer-side routers unless you
|
||||
* wish to have multiple bindings with different JavaScript function names. It
|
||||
* can be useful to have multiple browser-side routers with different client-
|
||||
* provided Handler instances when implementing different behaviors on a per-
|
||||
* browser basis.
|
||||
*
|
||||
* This implementation places no formatting restrictions on payload content.
|
||||
* An application may choose to exchange anything from simple formatted
|
||||
* strings to serialized XML or JSON data.
|
||||
*
|
||||
*
|
||||
* EXAMPLE USAGE
|
||||
*
|
||||
* 1. Define the router configuration. You can optionally specify settings
|
||||
* like the JavaScript function names. The configuration must be the same in
|
||||
* both the browser and renderer processes. If using multiple routers in the
|
||||
* same application make sure to specify unique function names for each
|
||||
* router configuration.
|
||||
*
|
||||
* // Example config object showing the default values.
|
||||
* CefMessageRouterConfig config = new CefMessageRouterConfig();
|
||||
* config.jsQueryFunction = "cefQuery";
|
||||
* config.jsCancelFunction = "cefQueryCancel";
|
||||
*
|
||||
* 2. Create an instance of CefMessageRouter in the browser process.
|
||||
*
|
||||
* messageRouter_ = CefMessageRouter.create(config);
|
||||
*
|
||||
* 3. Register one or more Handlers. The Handler instances must either outlive
|
||||
* the router or be removed from the router before they're deleted.
|
||||
*
|
||||
* messageRouter_.addHandler(myHandler);
|
||||
*
|
||||
* 4. Add your message router to all CefClient instances you want to get your
|
||||
* JavaScript code be handled.
|
||||
*
|
||||
* myClient.addMessageRouter(messageRouter_);
|
||||
*
|
||||
* 4. Execute the query function from JavaScript code.
|
||||
*
|
||||
* window.cefQuery({request: 'my_request',
|
||||
* persistent: false,
|
||||
* onSuccess: function(response) { print(response); },
|
||||
* onFailure: function(error_code, error_message) {} });
|
||||
*
|
||||
* 5. Handle the query in your CefMessageRouterHandler.onQuery implementation
|
||||
* and execute the appropriate callback either immediately or asynchronously.
|
||||
*
|
||||
* public boolean onQuery(CefBrowser browser,
|
||||
* long query_id,
|
||||
* String request,
|
||||
* boolean persistent,
|
||||
* CefQueryCallback callback) {
|
||||
* if (request.indexOf("my_request") == 0) {
|
||||
* callback.success("my_response");
|
||||
* return true;
|
||||
* }
|
||||
* return false; // Not handled.
|
||||
* }
|
||||
*
|
||||
* 6. Notice that the success callback is executed in JavaScript.
|
||||
*/
|
||||
public abstract class CefMessageRouter {
|
||||
private CefMessageRouterConfig routerConfig_ = null;
|
||||
|
||||
/**
|
||||
* Used to configure the query router. If using multiple router pairs make
|
||||
* sure to choose values that do not conflict.
|
||||
*/
|
||||
public static class CefMessageRouterConfig {
|
||||
/**
|
||||
* Name of the JavaScript function that will be added to the 'window' object
|
||||
* for sending a query. The default value is "cefQuery".
|
||||
*/
|
||||
public String jsQueryFunction;
|
||||
|
||||
/**
|
||||
* Name of the JavaScript function that will be added to the 'window' object
|
||||
* for canceling a pending query. The default value is "cefQueryCancel".
|
||||
*/
|
||||
public String jsCancelFunction;
|
||||
|
||||
public CefMessageRouterConfig() {
|
||||
this("cefQuery", "cefQueryCancel");
|
||||
}
|
||||
|
||||
public CefMessageRouterConfig(String queryFunction, String cancelFunction) {
|
||||
jsQueryFunction = queryFunction;
|
||||
jsCancelFunction = cancelFunction;
|
||||
}
|
||||
}
|
||||
|
||||
// This CTOR can't be called directly. Call method create() instead.
|
||||
CefMessageRouter() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
dispose();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new router with the default configuration. The addHandler() method should be called
|
||||
* to add a handler.
|
||||
*/
|
||||
public static final CefMessageRouter create() {
|
||||
return CefMessageRouter.create(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new router with the specified configuration. The addHandler() method should be
|
||||
* called to add a handler.
|
||||
*/
|
||||
public static final CefMessageRouter create(CefMessageRouterConfig config) {
|
||||
return CefMessageRouter.create(config, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new router with the specified handler and default configuration.
|
||||
*/
|
||||
public static final CefMessageRouter create(CefMessageRouterHandler handler) {
|
||||
return CefMessageRouter.create(null, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new router with the specified handler and configuration.
|
||||
*/
|
||||
public static final CefMessageRouter create(
|
||||
CefMessageRouterConfig config, CefMessageRouterHandler handler) {
|
||||
CefMessageRouter router = CefMessageRouter_N.createNative(config);
|
||||
if (router != null && handler != null) router.addHandler(handler, true);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called if the CefMessageRouter instance isn't used any more.
|
||||
*/
|
||||
public abstract void dispose();
|
||||
|
||||
// Called from native code during handling of createNative().
|
||||
void setMessageRouterConfig(CefMessageRouterConfig config) {
|
||||
routerConfig_ = config;
|
||||
}
|
||||
|
||||
// Called from native code during handling of CefClientHandler.[add|remove]MessageRouter().
|
||||
public final CefMessageRouterConfig getMessageRouterConfig() {
|
||||
return routerConfig_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new query handler.
|
||||
*
|
||||
* @param handler The handler to be added.
|
||||
* @param first If true the handler will be added as the first handler, otherwise it will be
|
||||
* added as the last handler.
|
||||
* @return True if the handler is added successfully.
|
||||
*/
|
||||
public abstract boolean addHandler(CefMessageRouterHandler handler, boolean first);
|
||||
|
||||
/**
|
||||
* Remove an existing query handler. Any pending queries associated with the handler will be
|
||||
* canceled. onQueryCanceled will be called and the associated JavaScript onFailure callback
|
||||
* will be executed with an error code of -1.
|
||||
*
|
||||
* @param handler The handler to be removed.
|
||||
* @return True if the handler is removed successfully.
|
||||
*/
|
||||
public abstract boolean removeHandler(CefMessageRouterHandler handler);
|
||||
|
||||
/**
|
||||
* Cancel all pending queries associated with either |browser| or |handler|. If both |browser|
|
||||
* and |handler| are NULL all pending queries will be canceled. onQueryCanceled will be called
|
||||
* and the associated JavaScript onFailure callback will be executed in all cases with an error
|
||||
* code of -1.
|
||||
*
|
||||
* @param browser The associated browser, or null.
|
||||
* @param handler The associated handler, or null.
|
||||
*/
|
||||
public abstract void cancelPending(CefBrowser browser, CefMessageRouterHandler handler);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.callback.CefNative;
|
||||
import org.cef.handler.CefMessageRouterHandler;
|
||||
|
||||
class CefMessageRouter_N extends CefMessageRouter implements CefNative {
|
||||
// Used internally to store a pointer to the CEF object.
|
||||
private long N_CefHandle = 0;
|
||||
|
||||
@Override
|
||||
public void setNativeRef(String identifer, long nativeRef) {
|
||||
N_CefHandle = nativeRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeRef(String identifer) {
|
||||
return N_CefHandle;
|
||||
}
|
||||
|
||||
private CefMessageRouter_N() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static final CefMessageRouter createNative(CefMessageRouterConfig config) {
|
||||
try {
|
||||
return CefMessageRouter_N.N_Create(config);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
N_Dispose(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addHandler(CefMessageRouterHandler handler, boolean first) {
|
||||
try {
|
||||
return N_AddHandler(N_CefHandle, handler, first);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeHandler(CefMessageRouterHandler handler) {
|
||||
try {
|
||||
return N_RemoveHandler(N_CefHandle, handler);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelPending(CefBrowser browser, CefMessageRouterHandler handler) {
|
||||
try {
|
||||
N_CancelPending(N_CefHandle, browser, handler);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native static CefMessageRouter_N N_Create(CefMessageRouterConfig config);
|
||||
private final native void N_Dispose(long self);
|
||||
private final native boolean N_AddHandler(
|
||||
long self, CefMessageRouterHandler handler, boolean first);
|
||||
private final native boolean N_RemoveHandler(long self, CefMessageRouterHandler handler);
|
||||
private final native void N_CancelPending(
|
||||
long self, CefBrowser browser, CefMessageRouterHandler handler);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.montoyo.mcef.MCEF;
|
||||
import net.montoyo.mcef.utilities.Log;
|
||||
import org.lwjgl.opengl.EXTBgra;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
public class CefRenderer {
|
||||
|
||||
//montoyo: debug tool
|
||||
//montoyo: debug tool
|
||||
private static final ArrayList<Integer> GL_TEXTURES = new ArrayList<>();
|
||||
|
||||
public static void dumpVRAMLeak() {
|
||||
Log.info(">>>>> MCEF: Beginning VRAM leak report");
|
||||
GL_TEXTURES.forEach(tex -> Log.warning(">>>>> MCEF: This texture has not been freed: " + tex));
|
||||
Log.info(">>>>> MCEF: End of VRAM leak report");
|
||||
}
|
||||
|
||||
private boolean transparent_;
|
||||
public int[] texture_id_ = new int[1];
|
||||
private int view_width_ = 0;
|
||||
private int view_height_ = 0;
|
||||
private float spin_x_ = 0f;
|
||||
private float spin_y_ = 0f;
|
||||
private Rectangle popup_rect_ = new Rectangle(0, 0, 0, 0);
|
||||
private Rectangle original_popup_rect_ = new Rectangle(0, 0, 0, 0);
|
||||
private boolean use_draw_pixels_ = false;
|
||||
|
||||
protected CefRenderer(boolean transparent) {
|
||||
transparent_ = transparent;
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected boolean isTransparent() {
|
||||
return transparent_;
|
||||
}
|
||||
|
||||
protected int getTextureID() {
|
||||
return texture_id_[0];
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
GlStateManager.enableTexture2D();
|
||||
texture_id_[0] = glGenTextures();
|
||||
|
||||
if (MCEF.CHECK_VRAM_LEAK)
|
||||
GL_TEXTURES.add(texture_id_[0]);
|
||||
|
||||
GlStateManager.bindTexture(texture_id_[0]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
GlStateManager.bindTexture(0);
|
||||
}
|
||||
|
||||
protected void cleanup() {
|
||||
if (texture_id_[0] != 0) {
|
||||
if (MCEF.CHECK_VRAM_LEAK)
|
||||
GL_TEXTURES.remove((Object) texture_id_[0]);
|
||||
|
||||
glDeleteTextures(texture_id_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
protected void render(double x1, double y1, double x2, double y2) {
|
||||
if (view_width_ == 0 || view_height_ == 0)
|
||||
return;
|
||||
|
||||
Tessellator t = Tessellator.getInstance();
|
||||
BufferBuilder vb = t.getBuffer();
|
||||
|
||||
GlStateManager.bindTexture(texture_id_[0]);
|
||||
vb.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
vb.pos(x1, y1, 0.0).tex(0.0, 1.0).color(255, 255, 255, 255).endVertex();
|
||||
vb.pos(x2, y1, 0.0).tex(1.f, 1.f).color(255, 255, 255, 255).endVertex();
|
||||
vb.pos(x2, y2, 0.0).tex(1.f, 0.0).color(255, 255, 255, 255).endVertex();
|
||||
vb.pos(x1, y2, 0.0).tex(0.0, 0.0).color(255, 255, 255, 255).endVertex();
|
||||
t.draw();
|
||||
GlStateManager.bindTexture(0);
|
||||
}
|
||||
|
||||
protected void onPopupSize(Rectangle rect) {
|
||||
if (rect.width <= 0 || rect.height <= 0) return;
|
||||
original_popup_rect_ = rect;
|
||||
popup_rect_ = getPopupRectInWebView(original_popup_rect_);
|
||||
}
|
||||
|
||||
protected Rectangle getPopupRect() {
|
||||
return (Rectangle) popup_rect_.clone();
|
||||
}
|
||||
|
||||
protected Rectangle getPopupRectInWebView(Rectangle original_rect) {
|
||||
Rectangle rc = original_rect;
|
||||
// if x or y are negative, move them to 0.
|
||||
if (rc.x < 0) rc.x = 0;
|
||||
if (rc.y < 0) rc.y = 0;
|
||||
// if popup goes outside the view, try to reposition origin
|
||||
if (rc.x + rc.width > view_width_) rc.x = view_width_ - rc.width;
|
||||
if (rc.y + rc.height > view_height_) rc.y = view_height_ - rc.height;
|
||||
// if x or y became negative, move them to 0 again.
|
||||
if (rc.x < 0) rc.x = 0;
|
||||
if (rc.y < 0) rc.y = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
protected void clearPopupRects() {
|
||||
popup_rect_.setBounds(0, 0, 0, 0);
|
||||
original_popup_rect_.setBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
protected void onPaint(boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height, boolean completeReRender) {
|
||||
if (transparent_) // Enable alpha blending.
|
||||
GlStateManager.enableBlend();
|
||||
|
||||
final int size = (width * height) << 2;
|
||||
if (size > buffer.limit()) {
|
||||
Log.warning("Bad data passed to CefRenderer.onPaint() triggered safe guards... (1)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable 2D textures.
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.bindTexture(texture_id_[0]);
|
||||
|
||||
int oldAlignement = glGetInteger(GL_UNPACK_ALIGNMENT);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
if (!popup) {
|
||||
if (completeReRender || width != view_width_ || height != view_height_) {
|
||||
// Update/resize the whole texture.
|
||||
view_width_ = width;
|
||||
view_height_ = height;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0, EXTBgra.GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
|
||||
} else {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_);
|
||||
|
||||
// Update just the dirty rectangles.
|
||||
for (Rectangle rect : dirtyRects) {
|
||||
if (rect.x < 0 || rect.y < 0 || rect.x + rect.width > view_width_ || rect.y + rect.height > view_height_)
|
||||
Log.warning("Bad data passed to CefRenderer.onPaint() triggered safe guards... (2)");
|
||||
else {
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width, rect.height, EXTBgra.GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
} else if (popup_rect_.width > 0 && popup_rect_.height > 0) {
|
||||
int skip_pixels = 0, x = popup_rect_.x;
|
||||
int skip_rows = 0, y = popup_rect_.y;
|
||||
int w = width;
|
||||
int h = height;
|
||||
|
||||
// Adjust the popup to fit inside the view.
|
||||
if (x < 0) {
|
||||
skip_pixels = -x;
|
||||
x = 0;
|
||||
}
|
||||
if (y < 0) {
|
||||
skip_rows = -y;
|
||||
y = 0;
|
||||
}
|
||||
if (x + w > view_width_)
|
||||
w -= x + w - view_width_;
|
||||
if (y + h > view_height_)
|
||||
h -= y + h - view_height_;
|
||||
|
||||
// Update the popup rectangle.
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, EXTBgra.GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, oldAlignement);
|
||||
GlStateManager.bindTexture(0);
|
||||
}
|
||||
|
||||
protected void setSpin(float spinX, float spinY) {
|
||||
spin_x_ = spinX;
|
||||
spin_y_ = spinY;
|
||||
}
|
||||
|
||||
protected void incrementSpin(float spinDX, float spinDY) {
|
||||
spin_x_ -= spinDX;
|
||||
spin_y_ -= spinDY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.handler.CefRequestContextHandler;
|
||||
|
||||
/**
|
||||
* A request context provides request handling for a set of related browser
|
||||
* objects. A request context is specified when creating a new browser object
|
||||
* via the CefClient.createBrowser method. Browser objects with different
|
||||
* request contexts will never be hosted in the same render process. Browser
|
||||
* objects with the same request context may or may not be hosted in the same
|
||||
* render process depending on the process model. Browser objects created
|
||||
* indirectly via the JavaScript window.open function or targeted links will
|
||||
* share the same render process and the same request context as the source
|
||||
* browser. When running in single-process mode there is only a single render
|
||||
* process (the main process) and so all browsers created in single-process mode
|
||||
* will share the same request context. This will be the first request context
|
||||
* passed into the CefClient.createBrowser method and all other request
|
||||
* context objects will be ignored.
|
||||
*/
|
||||
public abstract class CefRequestContext {
|
||||
// This CTOR can't be called directly. Call method create() instead.
|
||||
CefRequestContext() {}
|
||||
|
||||
/**
|
||||
* Returns the global context object.
|
||||
*/
|
||||
public static final CefRequestContext getGlobalContext() {
|
||||
return CefRequestContext_N.getGlobalContextNative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new context object with the specified handler.
|
||||
*/
|
||||
public static final CefRequestContext createContext(CefRequestContextHandler handler) {
|
||||
return CefRequestContext_N.createNative(handler);
|
||||
}
|
||||
|
||||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Returns true if this object is the global context.
|
||||
*/
|
||||
public abstract boolean isGlobal();
|
||||
|
||||
/**
|
||||
* Returns the handler for this context if any.
|
||||
*/
|
||||
public abstract CefRequestContextHandler getHandler();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser;
|
||||
|
||||
import org.cef.callback.CefNative;
|
||||
import org.cef.handler.CefRequestContextHandler;
|
||||
|
||||
class CefRequestContext_N extends CefRequestContext implements CefNative {
|
||||
// Used internally to store a pointer to the CEF object.
|
||||
private long N_CefHandle = 0;
|
||||
private static CefRequestContext_N globalInstance = null;
|
||||
private CefRequestContextHandler handler = null;
|
||||
|
||||
@Override
|
||||
public void setNativeRef(String identifer, long nativeRef) {
|
||||
N_CefHandle = nativeRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeRef(String identifer) {
|
||||
return N_CefHandle;
|
||||
}
|
||||
|
||||
CefRequestContext_N() {
|
||||
super();
|
||||
}
|
||||
|
||||
static final CefRequestContext_N getGlobalContextNative() {
|
||||
CefRequestContext_N result = null;
|
||||
try {
|
||||
result = CefRequestContext_N.N_GetGlobalContext();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
|
||||
if (globalInstance == null) {
|
||||
globalInstance = result;
|
||||
} else if (globalInstance.N_CefHandle == result.N_CefHandle) {
|
||||
result.N_CefRequestContext_DTOR();
|
||||
}
|
||||
return globalInstance;
|
||||
}
|
||||
|
||||
static final CefRequestContext_N createNative(CefRequestContextHandler handler) {
|
||||
CefRequestContext_N result = null;
|
||||
try {
|
||||
result = CefRequestContext_N.N_CreateContext(handler);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
if (result != null) result.handler = handler;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
N_CefRequestContext_DTOR();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobal() {
|
||||
try {
|
||||
return N_IsGlobal();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefRequestContextHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
private final static native CefRequestContext_N N_GetGlobalContext();
|
||||
private final static native CefRequestContext_N N_CreateContext(
|
||||
CefRequestContextHandler handler);
|
||||
private final native boolean N_IsGlobal();
|
||||
private final native void N_CefRequestContext_DTOR();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.browser.mac;
|
||||
|
||||
import org.cef.browser.CefBrowserWindow;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
public class CefBrowserWindowMac implements CefBrowserWindow {
|
||||
@Override
|
||||
public long getWindowHandle(Component comp) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
/**
|
||||
* Callback interface used for asynchronous continuation of authentication
|
||||
* requests.
|
||||
*/
|
||||
public interface CefAuthCallback {
|
||||
/**
|
||||
* Continue the authentication request.
|
||||
*/
|
||||
public void Continue(String username, String password);
|
||||
|
||||
/**
|
||||
* Cancel the authentication request.
|
||||
*/
|
||||
public void cancel();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
class CefAuthCallback_N extends CefNativeAdapter implements CefAuthCallback {
|
||||
CefAuthCallback_N() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
cancel();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void Continue(String username, String password) {
|
||||
try {
|
||||
N_Continue(getNativeRef(null), username, password);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
try {
|
||||
N_Cancel(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void N_Continue(long self, String username, String password);
|
||||
private final native void N_Cancel(long self);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
/**
|
||||
* Callback interface used to asynchronously continue a download.
|
||||
*/
|
||||
public interface CefBeforeDownloadCallback {
|
||||
/**
|
||||
* Call to continue the download.
|
||||
* @param downloadPath Set it to the full file path for the download
|
||||
* including the file name or leave blank to use the suggested name and
|
||||
* the default temp directory.
|
||||
* @param showDialog Set it to true if you do wish to show the default
|
||||
* "Save As" dialog.
|
||||
*/
|
||||
public void Continue(String downloadPath, boolean showDialog);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
class CefBeforeDownloadCallback_N extends CefNativeAdapter implements CefBeforeDownloadCallback {
|
||||
CefBeforeDownloadCallback_N() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
Continue("", false);
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void Continue(String downloadPath, boolean showDialog) {
|
||||
try {
|
||||
N_Continue(getNativeRef(null), downloadPath, showDialog);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void N_Continue(long self, String downloadPath, boolean showDialog);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
/**
|
||||
* Generic callback interface used for asynchronous continuation.
|
||||
*/
|
||||
public interface CefCallback {
|
||||
/**
|
||||
* Continue processing.
|
||||
*/
|
||||
void Continue();
|
||||
|
||||
/**
|
||||
* Cancel processing.
|
||||
*/
|
||||
void cancel();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
class CefCallback_N extends CefNativeAdapter implements CefCallback {
|
||||
CefCallback_N() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
cancel();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void Continue() {
|
||||
try {
|
||||
N_Continue(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
try {
|
||||
N_Cancel(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void N_Continue(long self);
|
||||
private final native void N_Cancel(long self);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Class used to create and/or parse command line arguments. Arguments with
|
||||
* '--', '-' and, on Windows, '/' prefixes are considered switches. Switches
|
||||
* will always precede any arguments without switch prefixes. Switches can
|
||||
* optionally have a value specified using the '=' delimiter (e.g.
|
||||
* "-switch=value"). An argument of "--" will terminate switch parsing with all
|
||||
* subsequent tokens, regardless of prefix, being interpreted as non-switch
|
||||
* arguments. Switch names are considered case-insensitive.
|
||||
*/
|
||||
public interface CefCommandLine {
|
||||
/**
|
||||
* Reset the command-line switches and arguments but leave the program
|
||||
* component unchanged.
|
||||
*/
|
||||
public void reset();
|
||||
|
||||
/**
|
||||
* Get the program part of the command line string (the first item).
|
||||
*/
|
||||
public String getProgram();
|
||||
|
||||
/**
|
||||
* Set the program part of the command line string (the first item).
|
||||
* @param program Name of the program.
|
||||
*/
|
||||
public void setProgram(String program);
|
||||
|
||||
/**
|
||||
* Checks if the command line has switches.
|
||||
* @return true if the command line has switches.
|
||||
*/
|
||||
public boolean hasSwitches();
|
||||
|
||||
/**
|
||||
* Checks if the command line has a specific switches.
|
||||
* @param name A switch name to test for.
|
||||
* @return true if the command line contains the given switch.
|
||||
*/
|
||||
public boolean hasSwitch(String name);
|
||||
|
||||
/**
|
||||
* Returns the value associated with the given switch. If the switch has no
|
||||
* value or isn't present this method returns the empty string.
|
||||
* @param name the name of the switch.
|
||||
* @return the value of the switch.
|
||||
*/
|
||||
public String getSwitchValue(String name);
|
||||
|
||||
/**
|
||||
* Returns the map of switch names and values. If a switch has no value an
|
||||
* empty string is returned.
|
||||
* @return Map of switches and each value.
|
||||
*/
|
||||
public Map<String, String> getSwitches();
|
||||
|
||||
/**
|
||||
* Add a switch with an empty value to the end of the command line.
|
||||
* @param name name of the switch.
|
||||
*/
|
||||
public void appendSwitch(String name);
|
||||
|
||||
/**
|
||||
* Add a switch with the specified value to the end of the command line.
|
||||
* @param name name of the switch.
|
||||
* @param value value for the switch.
|
||||
*/
|
||||
public void appendSwitchWithValue(String name, String value);
|
||||
|
||||
/**
|
||||
* Tests if there are remaining command line arguments.
|
||||
* @return True if there are remaining command line arguments.
|
||||
*/
|
||||
public boolean hasArguments();
|
||||
|
||||
/**
|
||||
* Get the remaining command line arguments.
|
||||
* @return Vector of command line arguments.
|
||||
*/
|
||||
public Vector<String> getArguments();
|
||||
|
||||
/**
|
||||
* Add an argument to the end of the command line.
|
||||
* @param argument name of the argument.
|
||||
*/
|
||||
public void appendArgument(String argument);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
class CefCommandLine_N extends CefNativeAdapter implements CefCommandLine {
|
||||
CefCommandLine_N() {}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
try {
|
||||
N_Reset(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProgram() {
|
||||
try {
|
||||
return N_GetProgram(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(String program) {
|
||||
try {
|
||||
N_SetProgram(getNativeRef(null), program);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSwitches() {
|
||||
try {
|
||||
return N_HasSwitches(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSwitch(String name) {
|
||||
try {
|
||||
return N_HasSwitch(getNativeRef(null), name);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSwitchValue(String name) {
|
||||
try {
|
||||
return N_GetSwitchValue(getNativeRef(null), name);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getSwitches() {
|
||||
try {
|
||||
return N_GetSwitches(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSwitch(String name) {
|
||||
try {
|
||||
N_AppendSwitch(getNativeRef(null), name);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSwitchWithValue(String name, String value) {
|
||||
try {
|
||||
N_AppendSwitchWithValue(getNativeRef(null), name, value);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasArguments() {
|
||||
try {
|
||||
return N_HasArguments(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<String> getArguments() {
|
||||
try {
|
||||
return N_GetArguments(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendArgument(String argument) {
|
||||
try {
|
||||
N_AppendArgument(getNativeRef(null), argument);
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = "CefCommandLine [program=\'" + getProgram() + "\'";
|
||||
if (hasSwitches()) {
|
||||
Map<String, String> switches = getSwitches();
|
||||
result += ", switches=" + switches;
|
||||
}
|
||||
if (hasArguments()) {
|
||||
Vector<String> arguments = getArguments();
|
||||
result += ", arguments=" + arguments;
|
||||
}
|
||||
return result + "]";
|
||||
}
|
||||
|
||||
private final native void N_Reset(long self);
|
||||
private final native String N_GetProgram(long self);
|
||||
private final native void N_SetProgram(long self, String program);
|
||||
private final native boolean N_HasSwitches(long self);
|
||||
private final native boolean N_HasSwitch(long self, String name);
|
||||
private final native String N_GetSwitchValue(long self, String name);
|
||||
private final native Map<String, String> N_GetSwitches(long self);
|
||||
private final native void N_AppendSwitch(long self, String name);
|
||||
private final native void N_AppendSwitchWithValue(long self, String name, String value);
|
||||
private final native boolean N_HasArguments(long self);
|
||||
private final native Vector<String> N_GetArguments(long self);
|
||||
private final native void N_AppendArgument(long self, String argument);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
/**
|
||||
* Generic callback interface used for asynchronous completion.
|
||||
*/
|
||||
public interface CefCompletionCallback {
|
||||
/**
|
||||
* Method that will be called once the task is complete.
|
||||
*/
|
||||
public abstract void onComplete();
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Provides information about the context menu state. The methods of this class
|
||||
* can only be accessed on browser process the UI thread.
|
||||
*/
|
||||
public interface CefContextMenuParams {
|
||||
/**
|
||||
* Supported context menu type flags.
|
||||
*/
|
||||
public static final class TypeFlags {
|
||||
public final static int CM_TYPEFLAG_NONE = 0; //!< No node is selected.
|
||||
public final static int CM_TYPEFLAG_PAGE = 1 << 0; //!< The top page is selected.
|
||||
public final static int CM_TYPEFLAG_FRAME = 1 << 1; //!< A subframe page is selected.
|
||||
public final static int CM_TYPEFLAG_LINK = 1 << 2; //!< A link is selected.
|
||||
public final static int CM_TYPEFLAG_MEDIA = 1 << 3; //!< A media node is selected.
|
||||
public final static int CM_TYPEFLAG_SELECTION = 1
|
||||
<< 4; //!< There is a textual or mixed selection that is selected.
|
||||
public final static int CM_TYPEFLAG_EDITABLE = 1 << 5; //!< An editable element is selected.
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported context menu media types.
|
||||
*/
|
||||
public enum MediaType {
|
||||
CM_MEDIATYPE_NONE, //!< No special node is in context.
|
||||
CM_MEDIATYPE_IMAGE, //!< An image node is selected.
|
||||
CM_MEDIATYPE_VIDEO, //!< A video node is selected.
|
||||
CM_MEDIATYPE_AUDIO, //!< An audio node is selected.
|
||||
CM_MEDIATYPE_FILE, //!< A file node is selected.
|
||||
CM_MEDIATYPE_PLUGIN, //!< A plugin node is selected.
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported context menu media state bit flags.
|
||||
*/
|
||||
public static final class MediaStateFlags {
|
||||
public final static int CM_MEDIAFLAG_NONE = 0;
|
||||
public final static int CM_MEDIAFLAG_ERROR = 1 << 0;
|
||||
public final static int CM_MEDIAFLAG_PAUSED = 1 << 1;
|
||||
public final static int CM_MEDIAFLAG_MUTED = 1 << 2;
|
||||
public final static int CM_MEDIAFLAG_LOOP = 1 << 3;
|
||||
public final static int CM_MEDIAFLAG_CAN_SAVE = 1 << 4;
|
||||
public final static int CM_MEDIAFLAG_HAS_AUDIO = 1 << 5;
|
||||
public final static int CM_MEDIAFLAG_HAS_VIDEO = 1 << 6;
|
||||
public final static int CM_MEDIAFLAG_CONTROL_ROOT_ELEMENT = 1 << 7;
|
||||
public final static int CM_MEDIAFLAG_CAN_PRINT = 1 << 8;
|
||||
public final static int CM_MEDIAFLAG_CAN_ROTATE = 1 << 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported context menu edit state bit flags.
|
||||
*/
|
||||
public static final class EditStateFlags {
|
||||
public final static int CM_EDITFLAG_NONE = 0;
|
||||
public final static int CM_EDITFLAG_CAN_UNDO = 1 << 0;
|
||||
public final static int CM_EDITFLAG_CAN_REDO = 1 << 1;
|
||||
public final static int CM_EDITFLAG_CAN_CUT = 1 << 2;
|
||||
public final static int CM_EDITFLAG_CAN_COPY = 1 << 3;
|
||||
public final static int CM_EDITFLAG_CAN_PASTE = 1 << 4;
|
||||
public final static int CM_EDITFLAG_CAN_DELETE = 1 << 5;
|
||||
public final static int CM_EDITFLAG_CAN_SELECT_ALL = 1 << 6;
|
||||
public final static int CM_EDITFLAG_CAN_TRANSLATE = 1 << 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the X coordinate of the mouse where the context menu was invoked.
|
||||
* Coords are relative to the associated RenderView's origin.
|
||||
*/
|
||||
int getXCoord();
|
||||
|
||||
/**
|
||||
* Returns the Y coordinate of the mouse where the context menu was invoked.
|
||||
* Coords are relative to the associated RenderView's origin.
|
||||
*/
|
||||
int getYCoord();
|
||||
|
||||
/**
|
||||
* Returns flags representing the type of node that the context menu was
|
||||
* invoked on. See TypeFlags for supported values
|
||||
*/
|
||||
int getTypeFlags();
|
||||
|
||||
/**
|
||||
* Returns the URL of the link, if any, that encloses the node that the
|
||||
* context menu was invoked on.
|
||||
*/
|
||||
String getLinkUrl();
|
||||
|
||||
/**
|
||||
* Returns the link URL, if any, to be used ONLY for "copy link address". We
|
||||
* don't validate this field in the frontend process.
|
||||
*/
|
||||
String getUnfilteredLinkUrl();
|
||||
|
||||
/**
|
||||
* Returns the source URL, if any, for the element that the context menu was
|
||||
* invoked on. Example of elements with source URLs are img, audio, and video.
|
||||
*/
|
||||
String getSourceUrl();
|
||||
|
||||
/**
|
||||
* Returns true if the context menu was invoked on an image which has
|
||||
* non-empty contents.
|
||||
*/
|
||||
boolean hasImageContents();
|
||||
|
||||
/**
|
||||
* Returns the URL of the top level page that the context menu was invoked on.
|
||||
*/
|
||||
String getPageUrl();
|
||||
|
||||
/**
|
||||
* Returns the URL of the subframe that the context menu was invoked on.
|
||||
*/
|
||||
String getFrameUrl();
|
||||
|
||||
/**
|
||||
* Returns the character encoding of the subframe that the context menu was
|
||||
* invoked on.
|
||||
*/
|
||||
String getFrameCharset();
|
||||
|
||||
/**
|
||||
* Returns the type of context node that the context menu was invoked on.
|
||||
*/
|
||||
MediaType getMediaType();
|
||||
|
||||
/**
|
||||
* Returns flags representing the actions supported by the media element, if
|
||||
* any, that the context menu was invoked on. See MediaStateFlags for possible
|
||||
* values.
|
||||
*/
|
||||
int getMediaStateFlags();
|
||||
|
||||
/**
|
||||
* Returns the text of the selection, if any, that the context menu was
|
||||
* invoked on.
|
||||
*/
|
||||
|
||||
String getSelectionText();
|
||||
|
||||
/**
|
||||
* Returns the text of the misspelled word, if any, that the context menu was
|
||||
* invoked on.
|
||||
*/
|
||||
String getMisspelledWord();
|
||||
|
||||
/**
|
||||
* Returns true if suggestions exist, false otherwise. Fills in |suggestions|
|
||||
* from the spell check service for the misspelled word if there is one.
|
||||
*/
|
||||
boolean getDictionarySuggestions(Vector<String> suggestions);
|
||||
|
||||
/**
|
||||
* Returns true if the context menu was invoked on an editable node.
|
||||
*/
|
||||
|
||||
boolean isEditable();
|
||||
|
||||
/**
|
||||
* Returns true if the context menu was invoked on an editable node where
|
||||
* spell-check is enabled.
|
||||
*/
|
||||
|
||||
boolean isSpellCheckEnabled();
|
||||
|
||||
/**
|
||||
* Returns flags representing the actions supported by the editable node, if
|
||||
* any, that the context menu was invoked on. See EditStateFlags for possible
|
||||
* values.
|
||||
*/
|
||||
int getEditStateFlags();
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
class CefContextMenuParams_N extends CefNativeAdapter implements CefContextMenuParams {
|
||||
CefContextMenuParams_N() {}
|
||||
|
||||
@Override
|
||||
public int getXCoord() {
|
||||
try {
|
||||
return N_GetXCoord(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getYCoord() {
|
||||
try {
|
||||
return N_GetYCoord(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTypeFlags() {
|
||||
try {
|
||||
return N_GetTypeFlags(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkUrl() {
|
||||
try {
|
||||
return N_GetLinkUrl(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUnfilteredLinkUrl() {
|
||||
try {
|
||||
return N_GetUnfilteredLinkUrl(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceUrl() {
|
||||
try {
|
||||
return N_GetSourceUrl(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasImageContents() {
|
||||
try {
|
||||
return N_HasImageContents(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPageUrl() {
|
||||
try {
|
||||
return N_GetPageUrl(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFrameUrl() {
|
||||
try {
|
||||
return N_GetFrameUrl(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFrameCharset() {
|
||||
try {
|
||||
return N_GetFrameCharset(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType getMediaType() {
|
||||
try {
|
||||
return N_GetMediaType(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediaStateFlags() {
|
||||
try {
|
||||
return N_GetMediaStateFlags(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectionText() {
|
||||
try {
|
||||
return N_GetSelectionText(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMisspelledWord() {
|
||||
try {
|
||||
return N_GetMisspelledWord(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDictionarySuggestions(Vector<String> suggestions) {
|
||||
try {
|
||||
return N_GetDictionarySuggestions(getNativeRef(null), suggestions);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
try {
|
||||
return N_IsEditable(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpellCheckEnabled() {
|
||||
try {
|
||||
return N_IsSpellCheckEnabled(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEditStateFlags() {
|
||||
try {
|
||||
return N_GetEditStateFlags(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private final native int N_GetXCoord(long self);
|
||||
private final native int N_GetYCoord(long self);
|
||||
private final native int N_GetTypeFlags(long self);
|
||||
private final native String N_GetLinkUrl(long self);
|
||||
private final native String N_GetUnfilteredLinkUrl(long self);
|
||||
private final native String N_GetSourceUrl(long self);
|
||||
private final native boolean N_HasImageContents(long self);
|
||||
private final native String N_GetPageUrl(long self);
|
||||
private final native String N_GetFrameUrl(long self);
|
||||
private final native String N_GetFrameCharset(long self);
|
||||
private final native MediaType N_GetMediaType(long self);
|
||||
private final native int N_GetMediaStateFlags(long self);
|
||||
private final native String N_GetSelectionText(long self);
|
||||
private final native String N_GetMisspelledWord(long self);
|
||||
private final native boolean N_GetDictionarySuggestions(long self, Vector<String> suggestions);
|
||||
private final native boolean N_IsEditable(long self);
|
||||
private final native boolean N_IsSpellCheckEnabled(long self);
|
||||
private final native int N_GetEditStateFlags(long self);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import org.cef.misc.BoolRef;
|
||||
import org.cef.network.CefCookie;
|
||||
|
||||
/**
|
||||
* Interface to implement for visiting cookie values. The methods of this class
|
||||
* will always be called on the IO thread.
|
||||
*/
|
||||
public interface CefCookieVisitor {
|
||||
/**
|
||||
* Method that will be called once for each cookie. |count| is the 0-based
|
||||
* index for the current cookie. |total| is the total number of cookies.
|
||||
* Set |deleteCookie| to true to delete the cookie currently being visited.
|
||||
* Return false to stop visiting cookies. This method may never be called if
|
||||
* no cookies are found.
|
||||
*/
|
||||
public abstract boolean visit(CefCookie cookie, int count, int total, BoolRef delete);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Class used to represent a download item.
|
||||
*/
|
||||
public interface CefDownloadItem {
|
||||
/**
|
||||
* Returns true if this object is valid. Do not call any other methods if this
|
||||
* function returns false.
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
/**
|
||||
* Returns true if the download is in progress.
|
||||
*/
|
||||
boolean isInProgress();
|
||||
|
||||
/**
|
||||
* Returns true if the download is complete.
|
||||
*/
|
||||
boolean isComplete();
|
||||
|
||||
/**
|
||||
* Returns true if the download has been canceled or interrupted.
|
||||
*/
|
||||
boolean isCanceled();
|
||||
|
||||
/**
|
||||
* Returns a simple speed estimate in bytes/s.
|
||||
*/
|
||||
long getCurrentSpeed();
|
||||
|
||||
/**
|
||||
* Returns the rough percent complete or -1 if the receive total size is
|
||||
* unknown.
|
||||
*/
|
||||
int getPercentComplete();
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes.
|
||||
*/
|
||||
long getTotalBytes();
|
||||
|
||||
/**
|
||||
* Returns the number of received bytes.
|
||||
*/
|
||||
long getReceivedBytes();
|
||||
|
||||
/**
|
||||
* Returns the time that the download started.
|
||||
*/
|
||||
Date getStartTime();
|
||||
|
||||
/**
|
||||
* Returns the time that the download ended.
|
||||
*/
|
||||
Date getEndTime();
|
||||
|
||||
/**
|
||||
* Returns the full path to the downloaded or downloading file.
|
||||
*/
|
||||
String getFullPath();
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this download.
|
||||
*/
|
||||
int getId();
|
||||
|
||||
/**
|
||||
* Returns the URL.
|
||||
*/
|
||||
String getURL();
|
||||
|
||||
/**
|
||||
* Returns the suggested file name.
|
||||
*/
|
||||
String getSuggestedFileName();
|
||||
|
||||
/**
|
||||
* Returns the content disposition.
|
||||
*/
|
||||
String getContentDisposition();
|
||||
|
||||
/**
|
||||
* Returns the mime type.
|
||||
*/
|
||||
String getMimeType();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
/**
|
||||
* Callback interface used to asynchronously modify download status.
|
||||
*/
|
||||
public interface CefDownloadItemCallback {
|
||||
/**
|
||||
* Call to cancel the download.
|
||||
*/
|
||||
public void cancel();
|
||||
|
||||
/**
|
||||
* Call to pause the download.
|
||||
*/
|
||||
public void pause();
|
||||
|
||||
/**
|
||||
* Call to resume the download.
|
||||
*/
|
||||
public void resume();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
class CefDownloadItemCallback_N extends CefNativeAdapter implements CefDownloadItemCallback {
|
||||
CefDownloadItemCallback_N() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
N_Dispose(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
try {
|
||||
N_Cancel(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
try {
|
||||
N_Pause(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
try {
|
||||
N_Resume(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void N_Dispose(long self);
|
||||
private final native void N_Cancel(long self);
|
||||
private final native void N_Pause(long self);
|
||||
private final native void N_Resume(long self);
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
class CefDownloadItem_N extends CefNativeAdapter implements CefDownloadItem {
|
||||
CefDownloadItem_N() {}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
try {
|
||||
return N_IsValid(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInProgress() {
|
||||
try {
|
||||
return N_IsInProgress(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
try {
|
||||
return N_IsComplete(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
try {
|
||||
return N_IsCanceled(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCurrentSpeed() {
|
||||
try {
|
||||
return N_GetCurrentSpeed(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPercentComplete() {
|
||||
try {
|
||||
return N_GetPercentComplete(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalBytes() {
|
||||
try {
|
||||
return N_GetTotalBytes(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getReceivedBytes() {
|
||||
try {
|
||||
return N_GetReceivedBytes(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getStartTime() {
|
||||
try {
|
||||
return N_GetStartTime(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getEndTime() {
|
||||
try {
|
||||
return N_GetEndTime(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullPath() {
|
||||
try {
|
||||
return N_GetFullPath(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
try {
|
||||
return N_GetId(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURL() {
|
||||
try {
|
||||
return N_GetURL(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuggestedFileName() {
|
||||
try {
|
||||
return N_GetSuggestedFileName(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentDisposition() {
|
||||
try {
|
||||
return N_GetContentDisposition(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
try {
|
||||
return N_GetMimeType(getNativeRef(null));
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final native boolean N_IsValid(long self);
|
||||
private final native boolean N_IsInProgress(long self);
|
||||
private final native boolean N_IsComplete(long self);
|
||||
private final native boolean N_IsCanceled(long self);
|
||||
private final native long N_GetCurrentSpeed(long self);
|
||||
private final native int N_GetPercentComplete(long self);
|
||||
private final native long N_GetTotalBytes(long self);
|
||||
private final native long N_GetReceivedBytes(long self);
|
||||
private final native Date N_GetStartTime(long self);
|
||||
private final native Date N_GetEndTime(long self);
|
||||
private final native String N_GetFullPath(long self);
|
||||
private final native int N_GetId(long self);
|
||||
private final native String N_GetURL(long self);
|
||||
private final native String N_GetSuggestedFileName(long self);
|
||||
private final native String N_GetContentDisposition(long self);
|
||||
private final native String N_GetMimeType(long self);
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Class used to represent drag data. The methods of this class may be called
|
||||
* on any thread.
|
||||
*/
|
||||
public abstract class CefDragData {
|
||||
/**
|
||||
* Supported drag operation bit flags.
|
||||
*/
|
||||
public static final class DragOperations {
|
||||
public final static int DRAG_OPERATION_NONE = 0;
|
||||
public final static int DRAG_OPERATION_COPY = 1;
|
||||
public final static int DRAG_OPERATION_LINK = 2;
|
||||
public final static int DRAG_OPERATION_GENERIC = 4;
|
||||
public final static int DRAG_OPERATION_PRIVATE = 8;
|
||||
public final static int DRAG_OPERATION_MOVE = 16;
|
||||
public final static int DRAG_OPERATION_DELETE = 32;
|
||||
public final static int DRAG_OPERATION_EVERY = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
// This CTOR can't be called directly. Call method create() instead.
|
||||
CefDragData() {}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
dispose();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CefDragData object.
|
||||
*/
|
||||
public static final CefDragData create() {
|
||||
return CefDragData_N.createNative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the current object
|
||||
*/
|
||||
public abstract CefDragData clone();
|
||||
|
||||
/**
|
||||
* Removes the native reference from an unused object.
|
||||
*/
|
||||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Test if the object is set to read-only.
|
||||
* @return true if this object is read-only.
|
||||
*/
|
||||
public abstract boolean isReadOnly();
|
||||
|
||||
/**
|
||||
* Returns true if the drag data is a link.
|
||||
*/
|
||||
public abstract boolean isLink();
|
||||
|
||||
/**
|
||||
* Returns true if the drag data is a text or html fragment.
|
||||
*/
|
||||
public abstract boolean isFragment();
|
||||
|
||||
/**
|
||||
* Returns true if the drag data is a file.
|
||||
*/
|
||||
public abstract boolean isFile();
|
||||
|
||||
/**
|
||||
* Return the link URL that is being dragged.
|
||||
*/
|
||||
public abstract String getLinkURL();
|
||||
|
||||
/**
|
||||
* Return the title associated with the link being dragged.
|
||||
*/
|
||||
public abstract String getLinkTitle();
|
||||
|
||||
/**
|
||||
* Return the metadata, if any, associated with the link being dragged.
|
||||
*/
|
||||
public abstract String getLinkMetadata();
|
||||
|
||||
/**
|
||||
* Return the plain text fragment that is being dragged.
|
||||
*/
|
||||
public abstract String getFragmentText();
|
||||
|
||||
/**
|
||||
* Return the text/html fragment that is being dragged.
|
||||
*/
|
||||
public abstract String getFragmentHtml();
|
||||
|
||||
/**
|
||||
* Return the base URL that the fragment came from. This value is used for
|
||||
* resolving relative URLs and may be empty.
|
||||
*/
|
||||
public abstract String getFragmentBaseURL();
|
||||
|
||||
/**
|
||||
* Write the contents of the file being dragged out of the web view into
|
||||
* |writer|. Returns the number of bytes sent to |writer|. If |writer| is
|
||||
* NULL this method will return the size of the file contents in bytes.
|
||||
* Call getFileName() to get a suggested name for the file.
|
||||
*
|
||||
* @param writer Writes the contents into this object.
|
||||
* @return The number of bytes sent to writer. If writer is NULL the size of
|
||||
* the file contents in bytes is returned.
|
||||
*/
|
||||
public abstract int getFileContents(OutputStream writer);
|
||||
|
||||
/**
|
||||
* Return the name of the file being dragged out of the browser window.
|
||||
*/
|
||||
public abstract String getFileName();
|
||||
|
||||
/**
|
||||
* Retrieve the list of file names that are being dragged into the browser
|
||||
* window.
|
||||
*/
|
||||
public abstract boolean getFileNames(Vector<String> names);
|
||||
|
||||
/**
|
||||
* Set the link URL that is being dragged.
|
||||
* @param url The link URL to be set.
|
||||
*/
|
||||
public abstract void setLinkURL(String url);
|
||||
|
||||
/**
|
||||
* Set the title associated with the link being dragged.
|
||||
* @param title The tile associated with the link.
|
||||
*/
|
||||
public abstract void setLinkTitle(String title);
|
||||
|
||||
/**
|
||||
* Set the metadata associated with the link being dragged.
|
||||
* @param data The metadata associated with the link.
|
||||
*/
|
||||
public abstract void setLinkMetadata(String data);
|
||||
|
||||
/**
|
||||
* Set the plain text fragment that is being dragged.
|
||||
* @param text The plain text fragment to be set.
|
||||
*/
|
||||
public abstract void setFragmentText(String text);
|
||||
|
||||
/**
|
||||
* Set the text/html fragment that is being dragged.
|
||||
* @param html The html fragment to be set.
|
||||
*/
|
||||
public abstract void setFragmentHtml(String html);
|
||||
|
||||
/**
|
||||
* Set the base URL that the fragment came from.
|
||||
* @param baseUrl The base URL to be set.
|
||||
*/
|
||||
public abstract void setFragmentBaseURL(String baseUrl);
|
||||
|
||||
/**
|
||||
* Reset the file contents. You should do this before calling
|
||||
* CefBrowser.dragTargetDragEnter as the web view does not allow us to
|
||||
* drag in this kind of data.
|
||||
*/
|
||||
public abstract void resetFileContents();
|
||||
|
||||
/**
|
||||
* Add a file that is being dragged into the webview.
|
||||
* @param path The file and path to be set.
|
||||
* @param displayName The name to be displayed.
|
||||
*/
|
||||
public abstract void addFile(String path, String displayName);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CefDragData [isReadOnly()=" + isReadOnly() + ", isLink()=" + isLink()
|
||||
+ ", isFragment()=" + isFragment() + ", isFile()=" + isFile() + ", getLinkURL()="
|
||||
+ getLinkURL() + ", getLinkTitle()=" + getLinkTitle() + ", getLinkMetadata()="
|
||||
+ getLinkMetadata() + ", getFragmentText()=" + getFragmentText()
|
||||
+ ", getFragmentHtml()=" + getFragmentHtml() + ", getFragmentBaseURL()="
|
||||
+ getFragmentBaseURL() + ", getFileName()=" + getFileName() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package org.cef.callback;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Vector;
|
||||
|
||||
class CefDragData_N extends CefDragData implements CefNative {
|
||||
// Used internally to store a pointer to the CEF object.
|
||||
private long N_CefHandle = 0;
|
||||
|
||||
@Override
|
||||
public void setNativeRef(String identifer, long nativeRef) {
|
||||
N_CefHandle = nativeRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeRef(String identifer) {
|
||||
return N_CefHandle;
|
||||
}
|
||||
|
||||
CefDragData_N() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static CefDragData createNative() {
|
||||
try {
|
||||
return CefDragData_N.N_Create();
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CefDragData clone() {
|
||||
try {
|
||||
return N_Clone(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
N_Dispose(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
try {
|
||||
return N_IsReadOnly(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLink() {
|
||||
try {
|
||||
return N_IsLink(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragment() {
|
||||
try {
|
||||
return N_IsFragment(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
try {
|
||||
return N_IsFile(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkURL() {
|
||||
try {
|
||||
return N_GetLinkURL(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkTitle() {
|
||||
try {
|
||||
return N_GetLinkTitle(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkMetadata() {
|
||||
try {
|
||||
return N_GetLinkMetadata(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentText() {
|
||||
try {
|
||||
return N_GetFragmentText(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentHtml() {
|
||||
try {
|
||||
return N_GetFragmentHtml(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentBaseURL() {
|
||||
try {
|
||||
return N_GetFragmentBaseURL(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileContents(OutputStream writer) {
|
||||
try {
|
||||
return N_GetFileContents(N_CefHandle, writer);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
try {
|
||||
return N_GetFileName(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFileNames(Vector<String> names) {
|
||||
try {
|
||||
return N_GetFileNames(N_CefHandle, names);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setLinkURL(String url) {
|
||||
try {
|
||||
N_SetLinkURL(N_CefHandle, url);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLinkTitle(String title) {
|
||||
try {
|
||||
N_SetLinkTitle(N_CefHandle, title);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLinkMetadata(String data) {
|
||||
try {
|
||||
N_SetLinkMetadata(N_CefHandle, data);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFragmentText(String text) {
|
||||
try {
|
||||
N_SetFragmentText(N_CefHandle, text);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFragmentHtml(String html) {
|
||||
try {
|
||||
N_SetFragmentHtml(N_CefHandle, html);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFragmentBaseURL(String baseUrl) {
|
||||
try {
|
||||
N_SetFragmentBaseURL(N_CefHandle, baseUrl);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetFileContents() {
|
||||
try {
|
||||
N_ResetFileContents(N_CefHandle);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void addFile(String path, String displayName) {
|
||||
try {
|
||||
N_AddFile(N_CefHandle, path, displayName);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
ule.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final native static CefDragData_N N_Create();
|
||||
private final native CefDragData_N N_Clone(long self);
|
||||
private final native void N_Dispose(long self);
|
||||
private final native boolean N_IsReadOnly(long self);
|
||||
private final native boolean N_IsLink(long self);
|
||||
private final native boolean N_IsFragment(long self);
|
||||
private final native boolean N_IsFile(long self);
|
||||
private final native String N_GetLinkURL(long self);
|
||||
private final native String N_GetLinkTitle(long self);
|
||||
private final native String N_GetLinkMetadata(long self);
|
||||
private final native String N_GetFragmentText(long self);
|
||||
private final native String N_GetFragmentHtml(long self);
|
||||
private final native String N_GetFragmentBaseURL(long self);
|
||||
private final native int N_GetFileContents(long self, OutputStream writer);
|
||||
private final native String N_GetFileName(long self);
|
||||
private final native boolean N_GetFileNames(long self, Vector<String> names);
|
||||
private final native void N_SetLinkURL(long self, String url);
|
||||
private final native void N_SetLinkTitle(long self, String title);
|
||||
private final native void N_SetLinkMetadata(long self, String data);
|
||||
private final native void N_SetFragmentText(long self, String text);
|
||||
private final native void N_SetFragmentHtml(long self, String html);
|
||||
private final native void N_SetFragmentBaseURL(long self, String baseUrl);
|
||||
private final native void N_ResetFileContents(long self);
|
||||
private final native void N_AddFile(long self, String path, String displayName);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Vector<String> names = new Vector<>();
|
||||
getFileNames(names);
|
||||
String fileNamesStr = "{";
|
||||
for (String s : names) fileNamesStr += s + ",";
|
||||
fileNamesStr += "}";
|
||||
|
||||
return "CefDragData_N [isLink()=" + isLink() + ", isFragment()=" + isFragment()
|
||||
+ ", isFile()=" + isFile() + ", getLinkURL()=" + getLinkURL()
|
||||
+ ", getLinkTitle()=" + getLinkTitle() + ", getLinkMetadata()=" + getLinkMetadata()
|
||||
+ ", getFragmentText()=" + getFragmentText() + ", getFragmentHtml()="
|
||||
+ getFragmentHtml() + ", getFragmentBaseURL()=" + getFragmentBaseURL()
|
||||
+ ", getFileName()=" + getFileName() + ", getFileNames(vector)=" + fileNamesStr
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
package org.cef.callback;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Callback interface for asynchronous continuation of file dialog requests.
|
||||
*/
|
||||
public interface CefFileDialogCallback {
|
||||
/**
|
||||
* Continue the file selection with the specified file_paths. This may be
|
||||
* a single value or a list of values depending on the dialog mode. An empty
|
||||
* value is treated the same as calling Cancel().
|
||||
*
|
||||
* @param selectedAcceptFilter 0-based index of the value selected from the
|
||||
* accept filters array passed to CefDialogHandler::OnFileDialog.
|
||||
* @param filePaths list of selected file paths or an empty list.
|
||||
*/
|
||||
public void Continue(int selectedAcceptFilter, Vector<String> filePaths);
|
||||
|
||||
/**
|
||||
* Cancel the file selection.
|
||||
*/
|
||||
public void Cancel();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user